]> git-server-git.apps.pok.os.sepia.ceph.com Git - ceph-ci.git/blob
a75127a93c48a590625c3b61f62dcd0d16bdea57
[ceph-ci.git] /
1 import { HttpParams } from '@angular/common/http';
2 import { Component, Input, OnInit, ViewChild } from '@angular/core';
3 import { AbstractControl, UntypedFormControl, Validators } from '@angular/forms';
4 import { ActivatedRoute, Router } from '@angular/router';
5
6 import { NgbActiveModal, NgbModalRef, NgbTypeahead } from '@ng-bootstrap/ng-bootstrap';
7 import { ListItem } from 'carbon-components-angular';
8 import _ from 'lodash';
9 import { forkJoin, merge, Observable, Subject, Subscription } from 'rxjs';
10 import { debounceTime, distinctUntilChanged, filter, map } from 'rxjs/operators';
11 import { Pool } from '~/app/ceph/pool/pool';
12 import { CreateRgwServiceEntitiesComponent } from '~/app/ceph/rgw/create-rgw-service-entities/create-rgw-service-entities.component';
13 import { RgwRealm, RgwZonegroup, RgwZone } from '~/app/ceph/rgw/models/rgw-multisite';
14
15 import { CephServiceService } from '~/app/shared/api/ceph-service.service';
16 import { HostService } from '~/app/shared/api/host.service';
17 import { PoolService } from '~/app/shared/api/pool.service';
18 import { RbdService } from '~/app/shared/api/rbd.service';
19 import { RgwMultisiteService } from '~/app/shared/api/rgw-multisite.service';
20 import { RgwRealmService } from '~/app/shared/api/rgw-realm.service';
21 import { RgwZoneService } from '~/app/shared/api/rgw-zone.service';
22 import { RgwZonegroupService } from '~/app/shared/api/rgw-zonegroup.service';
23 import { SelectMessages } from '~/app/shared/components/select/select-messages.model';
24 import { SelectOption } from '~/app/shared/components/select/select-option.model';
25 import {
26   ActionLabelsI18n,
27   TimerServiceInterval,
28   URLVerbs,
29   SSL_PROTOCOLS,
30   SSL_CIPHERS
31 } from '~/app/shared/constants/app.constants';
32 import { CdForm } from '~/app/shared/forms/cd-form';
33 import { CdFormBuilder } from '~/app/shared/forms/cd-form-builder';
34 import { CdFormGroup } from '~/app/shared/forms/cd-form-group';
35 import { CdValidators } from '~/app/shared/forms/cd-validators';
36 import { FinishedTask } from '~/app/shared/models/finished-task';
37 import { Host } from '~/app/shared/models/host.interface';
38 import { CephServiceSpec } from '~/app/shared/models/service.interface';
39 import { ModalService } from '~/app/shared/services/modal.service';
40 import { TaskWrapperService } from '~/app/shared/services/task-wrapper.service';
41 import { TimerService } from '~/app/shared/services/timer.service';
42 import { environment } from '~/environments/environment';
43
44 @Component({
45   selector: 'cd-service-form',
46   templateUrl: './service-form.component.html',
47   styleUrls: ['./service-form.component.scss']
48 })
49 export class ServiceFormComponent extends CdForm implements OnInit {
50   public sub = new Subscription();
51
52   readonly MDS_SVC_ID_PATTERN = /^[a-zA-Z_.-][a-zA-Z0-9_.-]*$/;
53   readonly SNMP_DESTINATION_PATTERN = /^[^\:]+:[0-9]/;
54   readonly SNMP_ENGINE_ID_PATTERN = /^[0-9A-Fa-f]{10,64}/g;
55   readonly INGRESS_SUPPORTED_SERVICE_TYPES = ['rgw', 'nfs'];
56   readonly SMB_CONFIG_URI_PATTERN = /^(http:|https:|rados:|rados:mon-config-key:)/;
57   readonly OAUTH2_ISSUER_URL_PATTERN = /^(https?:\/\/)?([a-zA-Z0-9-]+(\.[a-zA-Z0-9-]+)+)(:[0-9]{1,5})?(\/.*)?$/;
58   readonly SSL_CIPHERS_PATTERN = /^[a-zA-Z0-9\-:]+$/;
59   readonly DEFAULT_SSL_PROTOCOL_ITEM = [{ content: 'TLSv1.3', selected: true }];
60   @ViewChild(NgbTypeahead, { static: false })
61   typeahead: NgbTypeahead;
62
63   @Input() hiddenServices: string[] = [];
64
65   @Input() editing = false;
66
67   @Input() serviceName: string;
68
69   @Input() serviceType: string;
70
71   serviceForm: CdFormGroup;
72   action: string;
73   resource: string;
74   serviceTypes: string[] = [];
75   serviceIds: string[] = [];
76   hosts: any;
77   labels: string[];
78   labelClick = new Subject<string>();
79   labelFocus = new Subject<string>();
80   pools: Array<Pool>;
81   rbdPools: Array<Pool>;
82   services: Array<CephServiceSpec> = [];
83   pageURL: string;
84   serviceList: CephServiceSpec[];
85   multisiteInfo: object[] = [];
86   defaultRealmId = '';
87   defaultZonegroupId = '';
88   defaultZoneId = '';
89   realmList: RgwRealm[] = [];
90   zonegroupList: RgwZonegroup[] = [];
91   zoneList: RgwZone[] = [];
92   bsModalRef: NgbModalRef;
93   defaultZonegroup: RgwZonegroup;
94   showRealmCreationForm = false;
95   defaultsInfo: { defaultRealmName: string; defaultZonegroupName: string; defaultZoneName: string };
96   realmNames: string[];
97   zonegroupNames: string[];
98   zoneNames: string[];
99   smbFeaturesList = ['domain'];
100   currentURL: string;
101   port: number = 443;
102   sslProtocolsItems: Array<ListItem> = Object.values(SSL_PROTOCOLS).map((protocol) => ({
103     content: protocol,
104     selected: true
105   }));
106   sslCiphersItems: Array<ListItem> = Object.values(SSL_CIPHERS).map((cipher) => ({
107     content: cipher,
108     selected: false
109   }));
110   showMgmtGatewayMessage: boolean = false;
111
112   constructor(
113     public actionLabels: ActionLabelsI18n,
114     private cephServiceService: CephServiceService,
115     private formBuilder: CdFormBuilder,
116     private hostService: HostService,
117     private poolService: PoolService,
118     private rbdService: RbdService,
119     private router: Router,
120     private taskWrapperService: TaskWrapperService,
121     public timerService: TimerService,
122     public timerServiceVariable: TimerServiceInterval,
123     public rgwRealmService: RgwRealmService,
124     public rgwZonegroupService: RgwZonegroupService,
125     public rgwZoneService: RgwZoneService,
126     public rgwMultisiteService: RgwMultisiteService,
127     private route: ActivatedRoute,
128     public activeModal: NgbActiveModal,
129     public modalService: ModalService
130   ) {
131     super();
132     this.resource = $localize`service`;
133     this.hosts = {
134       options: [],
135       messages: new SelectMessages({
136         empty: $localize`There are no hosts.`,
137         filter: $localize`Filter hosts`
138       })
139     };
140     this.createForm();
141   }
142
143   createForm() {
144     this.serviceForm = this.formBuilder.group({
145       // Global
146       service_type: [null, [Validators.required]],
147       service_id: [
148         null,
149         [
150           CdValidators.composeIf(
151             {
152               service_type: 'mds'
153             },
154             [
155               Validators.required,
156               CdValidators.custom('mdsPattern', (value: string) => {
157                 if (_.isEmpty(value)) {
158                   return false;
159                 }
160                 return !this.MDS_SVC_ID_PATTERN.test(value);
161               })
162             ]
163           ),
164           CdValidators.requiredIf({
165             service_type: 'nfs'
166           }),
167           CdValidators.requiredIf({
168             service_type: 'iscsi'
169           }),
170           CdValidators.requiredIf({
171             service_type: 'nvmeof'
172           }),
173           CdValidators.requiredIf({
174             service_type: 'ingress'
175           }),
176           CdValidators.requiredIf({
177             service_type: 'smb'
178           }),
179           CdValidators.composeIf(
180             {
181               service_type: 'rgw'
182             },
183             [Validators.required]
184           ),
185           CdValidators.custom('uniqueName', (service_id: string) => {
186             return this.serviceIds && this.serviceIds.includes(service_id);
187           })
188         ]
189       ],
190       placement: ['hosts'],
191       label: [
192         null,
193         [
194           CdValidators.requiredIf({
195             placement: 'label',
196             unmanaged: false
197           })
198         ]
199       ],
200       hosts: [[]],
201       count: [null, [CdValidators.number(false)]],
202       unmanaged: [false],
203       // iSCSI
204       // NVMe/TCP
205       pool: [
206         null,
207         [
208           CdValidators.requiredIf({
209             service_type: 'iscsi'
210           }),
211           CdValidators.requiredIf({
212             service_type: 'nvmeof'
213           })
214         ]
215       ],
216       group: [
217         'default',
218         CdValidators.requiredIf({
219           service_type: 'nvmeof'
220         })
221       ],
222       enable_mtls: [false],
223       root_ca_cert: [
224         null,
225         [
226           CdValidators.composeIf(
227             {
228               service_type: 'nvmeof',
229               enable_mtls: true
230             },
231             [Validators.required]
232           )
233         ]
234       ],
235       client_cert: [
236         null,
237         [
238           CdValidators.composeIf(
239             {
240               service_type: 'nvmeof',
241               enable_mtls: true
242             },
243             [Validators.required]
244           )
245         ]
246       ],
247       client_key: [
248         null,
249         [
250           CdValidators.composeIf(
251             {
252               service_type: 'nvmeof',
253               enable_mtls: true
254             },
255             [Validators.required]
256           )
257         ]
258       ],
259       server_cert: [
260         null,
261         [
262           CdValidators.composeIf(
263             {
264               service_type: 'nvmeof',
265               enable_mtls: true
266             },
267             [Validators.required]
268           )
269         ]
270       ],
271       server_key: [
272         null,
273         [
274           CdValidators.composeIf(
275             {
276               service_type: 'nvmeof',
277               enable_mtls: true
278             },
279             [Validators.required]
280           )
281         ]
282       ],
283       // RGW
284       rgw_frontend_port: [null, [CdValidators.number(false)]],
285       realm_name: [null],
286       zonegroup_name: [null],
287       zone_name: [null],
288       // iSCSI
289       trusted_ip_list: [null],
290       api_port: [null, [CdValidators.number(false)]],
291       api_user: [
292         null,
293         [
294           CdValidators.requiredIf({
295             service_type: 'iscsi',
296             unmanaged: false
297           })
298         ]
299       ],
300       api_password: [
301         null,
302         [
303           CdValidators.requiredIf({
304             service_type: 'iscsi',
305             unmanaged: false
306           })
307         ]
308       ],
309       // smb
310       cluster_id: [
311         null,
312         [
313           CdValidators.requiredIf({
314             service_type: 'smb'
315           })
316         ]
317       ],
318       features: new CdFormGroup(
319         this.smbFeaturesList.reduce((acc: object, e) => {
320           acc[e] = new UntypedFormControl(false);
321           return acc;
322         }, {})
323       ),
324       config_uri: [
325         null,
326         [
327           CdValidators.composeIf(
328             {
329               service_type: 'smb'
330             },
331             [
332               Validators.required,
333               CdValidators.custom('configUriPattern', (value: string) => {
334                 if (_.isEmpty(value)) {
335                   return false;
336                 }
337                 return !this.SMB_CONFIG_URI_PATTERN.test(value);
338               })
339             ]
340           )
341         ]
342       ],
343       custom_dns: [null],
344       join_sources: [null],
345       user_sources: [null],
346       include_ceph_users: [null],
347       // Ingress
348       backend_service: [
349         null,
350         [
351           CdValidators.requiredIf({
352             service_type: 'ingress'
353           })
354         ]
355       ],
356       virtual_ip: [
357         null,
358         [
359           CdValidators.requiredIf({
360             service_type: 'ingress'
361           })
362         ]
363       ],
364       frontend_port: [
365         null,
366         [
367           CdValidators.number(false),
368           CdValidators.requiredIf({
369             service_type: 'ingress'
370           })
371         ]
372       ],
373       monitor_port: [
374         null,
375         [
376           CdValidators.number(false),
377           CdValidators.requiredIf({
378             service_type: 'ingress'
379           })
380         ]
381       ],
382       virtual_interface_networks: [null],
383       ssl_protocols: [this.DEFAULT_SSL_PROTOCOL_ITEM],
384       ssl_ciphers: [
385         null,
386         [
387           CdValidators.custom('invalidPattern', (ciphers: string) => {
388             if (_.isEmpty(ciphers)) {
389               return false;
390             }
391             return !this.SSL_CIPHERS_PATTERN.test(ciphers);
392           })
393         ]
394       ],
395       // RGW, Ingress & iSCSI
396       ssl: [false],
397       ssl_cert: [
398         '',
399         [
400           CdValidators.composeIf(
401             {
402               service_type: 'rgw',
403               unmanaged: false,
404               ssl: true
405             },
406             [Validators.required, CdValidators.pemCert()]
407           ),
408           CdValidators.composeIf(
409             {
410               service_type: 'iscsi',
411               unmanaged: false,
412               ssl: true
413             },
414             [Validators.required, CdValidators.sslCert()]
415           ),
416           CdValidators.composeIf(
417             {
418               service_type: 'ingress',
419               unmanaged: false,
420               ssl: true
421             },
422             [Validators.required, CdValidators.pemCert()]
423           ),
424           CdValidators.composeIf(
425             {
426               service_type: 'oauth2-proxy',
427               unmanaged: false,
428               ssl: true
429             },
430             [Validators.required, CdValidators.sslCert()]
431           ),
432           CdValidators.composeIf(
433             {
434               service_type: 'mgmt-gateway',
435               unmanaged: false,
436               ssl: false
437             },
438             [CdValidators.sslCert()]
439           )
440         ]
441       ],
442       ssl_key: [
443         '',
444         [
445           CdValidators.composeIf(
446             {
447               service_type: 'iscsi',
448               unmanaged: false,
449               ssl: true
450             },
451             [Validators.required, CdValidators.sslPrivKey()]
452           ),
453           CdValidators.composeIf(
454             {
455               service_type: 'oauth2-proxy',
456               unmanaged: false,
457               ssl: true
458             },
459             [Validators.required, CdValidators.sslPrivKey()]
460           ),
461           CdValidators.composeIf(
462             {
463               service_type: 'mgmt-gateway',
464               unmanaged: false,
465               ssl: false
466             },
467             [CdValidators.sslPrivKey()]
468           )
469         ]
470       ],
471       // mgmt-gateway
472       enable_auth: [null],
473       port: [443, [CdValidators.number(false)]],
474       // snmp-gateway
475       snmp_version: [
476         null,
477         [
478           CdValidators.requiredIf({
479             service_type: 'snmp-gateway'
480           })
481         ]
482       ],
483       snmp_destination: [
484         null,
485         {
486           validators: [
487             CdValidators.requiredIf({
488               service_type: 'snmp-gateway'
489             }),
490             CdValidators.custom('snmpDestinationPattern', (value: string) => {
491               if (_.isEmpty(value)) {
492                 return false;
493               }
494               return !this.SNMP_DESTINATION_PATTERN.test(value);
495             })
496           ]
497         }
498       ],
499       engine_id: [
500         null,
501         [
502           CdValidators.requiredIf({
503             service_type: 'snmp-gateway'
504           }),
505           CdValidators.custom('snmpEngineIdPattern', (value: string) => {
506             if (_.isEmpty(value)) {
507               return false;
508             }
509             return !this.SNMP_ENGINE_ID_PATTERN.test(value);
510           })
511         ]
512       ],
513       auth_protocol: [
514         'SHA',
515         [
516           CdValidators.requiredIf({
517             service_type: 'snmp-gateway'
518           })
519         ]
520       ],
521       privacy_protocol: [null],
522       snmp_community: [
523         null,
524         [
525           CdValidators.requiredIf({
526             snmp_version: 'V2c'
527           })
528         ]
529       ],
530       snmp_v3_auth_username: [
531         null,
532         [
533           CdValidators.requiredIf({
534             service_type: 'snmp-gateway'
535           })
536         ]
537       ],
538       snmp_v3_auth_password: [
539         null,
540         [
541           CdValidators.requiredIf({
542             service_type: 'snmp-gateway'
543           })
544         ]
545       ],
546       snmp_v3_priv_password: [
547         null,
548         [
549           CdValidators.requiredIf({
550             privacy_protocol: { op: '!empty' }
551           })
552         ]
553       ],
554       grafana_port: [null, [CdValidators.number(false)]],
555       grafana_admin_password: [null],
556       // oauth2-proxy
557       provider_display_name: [
558         'My OIDC provider',
559         [
560           CdValidators.requiredIf({
561             service_type: 'oauth2-proxy'
562           })
563         ]
564       ],
565       client_id: [
566         null,
567         [
568           CdValidators.requiredIf({
569             service_type: 'oauth2-proxy'
570           })
571         ]
572       ],
573       client_secret: [
574         null,
575         [
576           CdValidators.requiredIf({
577             service_type: 'oauth2-proxy'
578           })
579         ]
580       ],
581       oidc_issuer_url: [
582         null,
583         [
584           CdValidators.requiredIf({
585             service_type: 'oauth2-proxy'
586           }),
587           CdValidators.custom('validUrl', (url: string) => {
588             if (_.isEmpty(url)) {
589               return false;
590             }
591             return !this.OAUTH2_ISSUER_URL_PATTERN.test(url);
592           })
593         ]
594       ],
595       https_address: [null, [CdValidators.oauthAddressTest()]],
596       redirect_url: [null],
597       allowlist_domains: [null]
598     });
599   }
600
601   resolveRoute() {
602     if (this.router.url.includes('services/(modal:create')) {
603       this.pageURL = 'services';
604       this.route.params.subscribe((params: { type: string }) => {
605         if (params?.type) {
606           this.serviceType = params.type;
607           this.serviceForm.get('service_type').setValue(this.serviceType);
608         }
609       });
610     } else if (this.router.url.includes('services/(modal:edit')) {
611       this.editing = true;
612       this.pageURL = 'services';
613       this.route.params.subscribe((params: { type: string; name: string }) => {
614         this.serviceName = params.name;
615         this.serviceType = params.type;
616       });
617     }
618   }
619
620   ngOnInit(): void {
621     this.action = this.actionLabels.CREATE;
622     this.resolveRoute();
623
624     this.cephServiceService
625       .list(new HttpParams({ fromObject: { limit: -1, offset: 0 } }))
626       .observable.subscribe((services: CephServiceSpec[]) => {
627         this.serviceList = services;
628         this.services = services.filter((service: any) =>
629           this.INGRESS_SUPPORTED_SERVICE_TYPES.includes(service.service_type)
630         );
631       });
632
633     this.cephServiceService.getKnownTypes().subscribe((resp: Array<string>) => {
634       // Remove service types:
635       // osd       - This is deployed a different way.
636       // container - This should only be used in the CLI.
637       // nvmeof    - This is only supported for IBM builds.
638       this.hiddenServices.push('osd', 'container', 'promtail');
639       if (environment.build !== 'ibm') this.hiddenServices.push('nvmeof');
640
641       this.serviceTypes = _.difference(resp, this.hiddenServices).sort();
642     });
643     this.hostService.getAllHosts().subscribe((resp: Host[]) => {
644       const options: SelectOption[] = [];
645       _.forEach(resp, (host: Host) => {
646         if (_.get(host, 'sources.orchestrator', false)) {
647           const option = new SelectOption(false, _.get(host, 'hostname'), '');
648           options.push(option);
649         }
650       });
651       this.hosts.options = [...options];
652     });
653     this.hostService.getLabels().subscribe((resp: string[]) => {
654       this.labels = resp;
655     });
656     this.poolService.getList().subscribe((resp: Pool[]) => {
657       this.pools = resp;
658       this.rbdPools = this.pools.filter(this.rbdService.isRBDPool);
659       if (!this.editing && this.serviceType) {
660         this.onServiceTypeChange(this.serviceType);
661       }
662     });
663
664     if (this.editing) {
665       this.action = this.actionLabels.EDIT;
666       this.disableForEditing(this.serviceType);
667       this.cephServiceService
668         .list(new HttpParams({ fromObject: { limit: -1, offset: 0 } }), this.serviceName)
669         .observable.subscribe((response: CephServiceSpec[]) => {
670           const formKeys = ['service_type', 'service_id', 'unmanaged'];
671           formKeys.forEach((keys) => {
672             this.serviceForm.get(keys).setValue(response[0][keys]);
673           });
674           if (!response[0]['unmanaged']) {
675             const placementKey = Object.keys(response[0]['placement'])[0];
676             let placementValue: string;
677             ['hosts', 'label'].indexOf(placementKey) >= 0
678               ? (placementValue = placementKey)
679               : (placementValue = 'hosts');
680             this.serviceForm.get('placement').setValue(placementValue);
681             this.serviceForm.get('count').setValue(response[0]['placement']['count']);
682             if (response[0]?.placement[placementValue]) {
683               this.serviceForm.get(placementValue).setValue(response[0]?.placement[placementValue]);
684             }
685           }
686           switch (this.serviceType) {
687             case 'iscsi':
688               const specKeys = ['pool', 'api_password', 'api_user', 'trusted_ip_list', 'api_port'];
689               specKeys.forEach((key) => {
690                 this.serviceForm.get(key).setValue(response[0].spec[key]);
691               });
692               this.serviceForm.get('ssl').setValue(response[0].spec?.api_secure);
693               if (response[0].spec?.api_secure) {
694                 this.serviceForm.get('ssl_cert').setValue(response[0].spec?.ssl_cert);
695                 this.serviceForm.get('ssl_key').setValue(response[0].spec?.ssl_key);
696               }
697               break;
698             case 'nvmeof':
699               this.serviceForm.get('pool').setValue(response[0].spec.pool);
700               this.serviceForm.get('group').setValue(response[0].spec.group);
701               this.serviceForm.get('enable_mtls').setValue(response[0].spec?.enable_auth);
702               this.serviceForm.get('root_ca_cert').setValue(response[0].spec?.root_ca_cert);
703               this.serviceForm.get('client_cert').setValue(response[0].spec?.client_cert);
704               this.serviceForm.get('client_key').setValue(response[0].spec?.client_key);
705               this.serviceForm.get('server_cert').setValue(response[0].spec?.server_cert);
706               this.serviceForm.get('server_key').setValue(response[0].spec?.server_key);
707               break;
708             case 'rgw':
709               this.serviceForm
710                 .get('rgw_frontend_port')
711                 .setValue(response[0].spec?.rgw_frontend_port);
712               this.setRgwFields(
713                 response[0].spec?.rgw_realm,
714                 response[0].spec?.rgw_zonegroup,
715                 response[0].spec?.rgw_zone
716               );
717               this.serviceForm.get('ssl').setValue(response[0].spec?.ssl);
718               if (response[0].spec?.ssl) {
719                 this.serviceForm
720                   .get('ssl_cert')
721                   .setValue(response[0].spec?.rgw_frontend_ssl_certificate);
722               }
723               break;
724             case 'ingress':
725               const ingressSpecKeys = [
726                 'backend_service',
727                 'virtual_ip',
728                 'frontend_port',
729                 'monitor_port',
730                 'virtual_interface_networks',
731                 'ssl'
732               ];
733               ingressSpecKeys.forEach((key) => {
734                 this.serviceForm.get(key).setValue(response[0].spec[key]);
735               });
736               if (response[0].spec?.ssl) {
737                 this.serviceForm.get('ssl_cert').setValue(response[0].spec?.ssl_cert);
738                 this.serviceForm.get('ssl_key').setValue(response[0].spec?.ssl_key);
739               }
740               break;
741             case 'mgmt-gateway':
742               let hrefSplitted = window.location.href.split(':');
743               this.currentURL = hrefSplitted[0] + hrefSplitted[1];
744               this.port = response[0].spec?.port;
745
746               if (response[0].spec?.ssl_protocols) {
747                 let selectedValues: Array<ListItem> = [];
748                 for (const value of response[0].spec.ssl_protocols) {
749                   selectedValues.push({ content: value, selected: true });
750                 }
751                 this.serviceForm.get('ssl_protocols').setValue(selectedValues);
752               }
753               if (response[0].spec?.ssl_ciphers) {
754                 this.serviceForm
755                   .get('ssl_ciphers')
756                   .setValue(response[0].spec?.ssl_ciphers.join(':'));
757               }
758               if (response[0].spec?.ssl_cert) {
759                 this.serviceForm.get('ssl_cert').setValue(response[0].spec.ssl_cert);
760               }
761               if (response[0].spec?.ssl_key) {
762                 this.serviceForm.get('ssl_key').setValue(response[0].spec.ssl_key);
763               }
764               if (response[0].spec?.enable_auth) {
765                 this.serviceForm.get('enable_auth').setValue(response[0].spec.enable_auth);
766               }
767               if (response[0].spec?.port) {
768                 this.serviceForm.get('port').setValue(response[0].spec.port);
769               }
770               break;
771             case 'smb':
772               const smbSpecKeys = [
773                 'cluster_id',
774                 'config_uri',
775                 'features',
776                 'join_sources',
777                 'user_sources',
778                 'custom_dns',
779                 'include_ceph_users'
780               ];
781               smbSpecKeys.forEach((key) => {
782                 if (key === 'features') {
783                   if (response[0].spec?.features) {
784                     response[0].spec.features.forEach((feature) => {
785                       this.serviceForm.get(`features.${feature}`).setValue(true);
786                     });
787                   }
788                 } else {
789                   this.serviceForm.get(key).setValue(response[0].spec[key]);
790                 }
791               });
792               break;
793             case 'snmp-gateway':
794               const snmpCommonSpecKeys = ['snmp_version', 'snmp_destination'];
795               snmpCommonSpecKeys.forEach((key) => {
796                 this.serviceForm.get(key).setValue(response[0].spec[key]);
797               });
798               if (this.serviceForm.getValue('snmp_version') === 'V3') {
799                 const snmpV3SpecKeys = [
800                   'engine_id',
801                   'auth_protocol',
802                   'privacy_protocol',
803                   'snmp_v3_auth_username',
804                   'snmp_v3_auth_password',
805                   'snmp_v3_priv_password'
806                 ];
807                 snmpV3SpecKeys.forEach((key) => {
808                   if (key !== null) {
809                     if (
810                       key === 'snmp_v3_auth_username' ||
811                       key === 'snmp_v3_auth_password' ||
812                       key === 'snmp_v3_priv_password'
813                     ) {
814                       this.serviceForm.get(key).setValue(response[0].spec['credentials'][key]);
815                     } else {
816                       this.serviceForm.get(key).setValue(response[0].spec[key]);
817                     }
818                   }
819                 });
820               } else {
821                 this.serviceForm
822                   .get('snmp_community')
823                   .setValue(response[0].spec['credentials']['snmp_community']);
824               }
825               break;
826             case 'grafana':
827               this.serviceForm.get('grafana_port').setValue(response[0].spec.port);
828               this.serviceForm
829                 .get('grafana_admin_password')
830                 .setValue(response[0].spec.initial_admin_password);
831               break;
832             case 'oauth2-proxy':
833               const oauth2SpecKeys = [
834                 'https_address',
835                 'provider_display_name',
836                 'client_id',
837                 'client_secret',
838                 'oidc_issuer_url',
839                 'redirect_url',
840                 'allowlist_domains'
841               ];
842               oauth2SpecKeys.forEach((key) => {
843                 this.serviceForm.get(key).setValue(response[0].spec[key]);
844               });
845               if (response[0].spec?.ssl) {
846                 this.serviceForm.get('ssl_cert').setValue(response[0].spec?.ssl_cert);
847                 this.serviceForm.get('ssl_key').setValue(response[0].spec?.ssl_key);
848               }
849           }
850         });
851     }
852     this.detectChanges();
853   }
854
855   detectChanges(): void {
856     const service_type = this.serviceForm.get('service_type');
857     if (service_type) {
858       service_type.valueChanges.subscribe((value) => {
859         if (value === 'mgmt-gateway') {
860           const port = this.serviceForm.get('port');
861           if (port) {
862             port.valueChanges.subscribe((_) => {
863               this.showMgmtGatewayMessage = true;
864             });
865           }
866           const ssl_protocols = this.serviceForm.get('ssl_protocols');
867           if (ssl_protocols) {
868             ssl_protocols.valueChanges.subscribe((_) => {
869               this.showMgmtGatewayMessage = true;
870             });
871           }
872           const ssl_ciphers = this.serviceForm.get('ssl_ciphers');
873           if (ssl_ciphers) {
874             ssl_ciphers.valueChanges.subscribe((_) => {
875               this.showMgmtGatewayMessage = true;
876             });
877           }
878         }
879       });
880     }
881   }
882
883   getDefaultsEntitiesForRgw(
884     defaultRealmId: string,
885     defaultZonegroupId: string,
886     defaultZoneId: string
887   ): { defaultRealmName: string; defaultZonegroupName: string; defaultZoneName: string } {
888     const defaultRealm = this.realmList.find((x: { id: string }) => x.id === defaultRealmId);
889     const defaultZonegroup = this.zonegroupList.find(
890       (x: { id: string }) => x.id === defaultZonegroupId
891     );
892     const defaultZone = this.zoneList.find((x: { id: string }) => x.id === defaultZoneId);
893     const defaultRealmName = defaultRealm !== undefined ? defaultRealm.name : null;
894     const defaultZonegroupName = defaultZonegroup !== undefined ? defaultZonegroup.name : 'default';
895     const defaultZoneName = defaultZone !== undefined ? defaultZone.name : 'default';
896     if (defaultZonegroupName === 'default' && !this.zonegroupNames.includes(defaultZonegroupName)) {
897       const defaultZonegroup = new RgwZonegroup();
898       defaultZonegroup.name = 'default';
899       this.zonegroupList.push(defaultZonegroup);
900     }
901     if (defaultZoneName === 'default' && !this.zoneNames.includes(defaultZoneName)) {
902       const defaultZone = new RgwZone();
903       defaultZone.name = 'default';
904       this.zoneList.push(defaultZone);
905     }
906     return {
907       defaultRealmName: defaultRealmName,
908       defaultZonegroupName: defaultZonegroupName,
909       defaultZoneName: defaultZoneName
910     };
911   }
912
913   getDefaultPlacementCount(serviceType: string) {
914     /**
915      * `defaults` from src/pybind/mgr/cephadm/module.py
916      */
917     switch (serviceType) {
918       case 'mon':
919         this.serviceForm.get('count').setValue(5);
920         break;
921       case 'mgr':
922       case 'mds':
923       case 'rgw':
924       case 'ingress':
925       case 'rbd-mirror':
926         this.serviceForm.get('count').setValue(2);
927         break;
928       case 'iscsi':
929       case 'cephfs-mirror':
930       case 'nfs':
931       case 'grafana':
932       case 'alertmanager':
933       case 'prometheus':
934       case 'loki':
935       case 'container':
936       case 'snmp-gateway':
937       case 'elastic-serach':
938       case 'jaeger-collector':
939       case 'jaeger-query':
940       case 'smb':
941       case 'oauth2-proxy':
942       case 'mgmt-gateway':
943         this.serviceForm.get('count').setValue(1);
944         break;
945       default:
946         this.serviceForm.get('count').setValue(null);
947     }
948   }
949
950   setRgwFields(realm_name?: string, zonegroup_name?: string, zone_name?: string) {
951     const observables = [
952       this.rgwRealmService.getAllRealmsInfo(),
953       this.rgwZonegroupService.getAllZonegroupsInfo(),
954       this.rgwZoneService.getAllZonesInfo()
955     ];
956     this.sub = forkJoin(observables).subscribe(
957       (multisiteInfo: [object, object, object]) => {
958         this.multisiteInfo = multisiteInfo;
959         this.realmList =
960           this.multisiteInfo[0] !== undefined && this.multisiteInfo[0].hasOwnProperty('realms')
961             ? this.multisiteInfo[0]['realms']
962             : [];
963         this.zonegroupList =
964           this.multisiteInfo[1] !== undefined && this.multisiteInfo[1].hasOwnProperty('zonegroups')
965             ? this.multisiteInfo[1]['zonegroups']
966             : [];
967         this.zoneList =
968           this.multisiteInfo[2] !== undefined && this.multisiteInfo[2].hasOwnProperty('zones')
969             ? this.multisiteInfo[2]['zones']
970             : [];
971         this.realmNames = this.realmList.map((realm) => {
972           return realm['name'];
973         });
974         this.zonegroupNames = this.zonegroupList.map((zonegroup) => {
975           return zonegroup['name'];
976         });
977         this.zoneNames = this.zoneList.map((zone) => {
978           return zone['name'];
979         });
980         this.defaultRealmId = multisiteInfo[0]['default_realm'];
981         this.defaultZonegroupId = multisiteInfo[1]['default_zonegroup'];
982         this.defaultZoneId = multisiteInfo[2]['default_zone'];
983         this.defaultsInfo = this.getDefaultsEntitiesForRgw(
984           this.defaultRealmId,
985           this.defaultZonegroupId,
986           this.defaultZoneId
987         );
988         if (!this.editing) {
989           this.serviceForm.get('realm_name').setValue(this.defaultsInfo['defaultRealmName']);
990           this.serviceForm
991             .get('zonegroup_name')
992             .setValue(this.defaultsInfo['defaultZonegroupName']);
993           this.serviceForm.get('zone_name').setValue(this.defaultsInfo['defaultZoneName']);
994         } else {
995           if (realm_name && !this.realmNames.includes(realm_name)) {
996             const realm = new RgwRealm();
997             realm.name = realm_name;
998             this.realmList.push(realm);
999           }
1000           if (zonegroup_name && !this.zonegroupNames.includes(zonegroup_name)) {
1001             const zonegroup = new RgwZonegroup();
1002             zonegroup.name = zonegroup_name;
1003             this.zonegroupList.push(zonegroup);
1004           }
1005           if (zone_name && !this.zoneNames.includes(zone_name)) {
1006             const zone = new RgwZone();
1007             zone.name = zone_name;
1008             this.zoneList.push(zone);
1009           }
1010           if (zonegroup_name === undefined && zone_name === undefined) {
1011             zonegroup_name = 'default';
1012             zone_name = 'default';
1013           }
1014           this.serviceForm.get('realm_name').setValue(realm_name);
1015           this.serviceForm.get('zonegroup_name').setValue(zonegroup_name);
1016           this.serviceForm.get('zone_name').setValue(zone_name);
1017         }
1018         if (this.realmList.length === 0) {
1019           this.showRealmCreationForm = true;
1020         } else {
1021           this.showRealmCreationForm = false;
1022         }
1023       },
1024       (_error) => {
1025         const defaultZone = new RgwZone();
1026         defaultZone.name = 'default';
1027         const defaultZonegroup = new RgwZonegroup();
1028         defaultZonegroup.name = 'default';
1029         this.zoneList.push(defaultZone);
1030         this.zonegroupList.push(defaultZonegroup);
1031       }
1032     );
1033   }
1034
1035   setNvmeServiceId() {
1036     const pool = this.serviceForm.get('pool').value;
1037     const group = this.serviceForm.get('group').value;
1038     if (pool && group) {
1039       this.serviceForm.get('service_id').setValue(`${pool}.${group}`);
1040     } else if (pool) {
1041       this.serviceForm.get('service_id').setValue(pool);
1042     } else if (group) {
1043       this.serviceForm.get('service_id').setValue(group);
1044     } else {
1045       this.serviceForm.get('service_id').setValue(null);
1046     }
1047   }
1048
1049   setNvmeDefaultPool() {
1050     const defaultPool =
1051       this.rbdPools?.find((p: Pool) => p.pool_name === 'rbd')?.pool_name ||
1052       this.rbdPools?.[0].pool_name;
1053     this.serviceForm.get('pool').setValue(defaultPool);
1054   }
1055
1056   requiresServiceId(serviceType: string) {
1057     return ['mds', 'rgw', 'nfs', 'iscsi', 'nvmeof', 'smb', 'ingress'].includes(serviceType);
1058   }
1059
1060   setServiceId(serviceId: string): void {
1061     const requiresServiceId: boolean = this.requiresServiceId(serviceId);
1062     if (requiresServiceId && serviceId === 'nvmeof') {
1063       this.setNvmeDefaultPool();
1064       this.setNvmeServiceId();
1065     } else if (requiresServiceId) {
1066       this.serviceForm.get('service_id').setValue(null);
1067     } else {
1068       this.serviceForm.get('service_id').setValue(serviceId);
1069     }
1070   }
1071
1072   onServiceTypeChange(selectedServiceType: string) {
1073     this.setServiceId(selectedServiceType);
1074
1075     this.serviceIds = this.serviceList
1076       ?.filter((service) => service['service_type'] === selectedServiceType)
1077       .map((service) => service['service_id']);
1078
1079     this.getDefaultPlacementCount(selectedServiceType);
1080
1081     if (selectedServiceType === 'rgw') {
1082       this.setRgwFields();
1083     }
1084     if (selectedServiceType === 'mgmt-gateway') {
1085       let hrefSplitted = window.location.href.split(':');
1086       this.currentURL = hrefSplitted[0] + hrefSplitted[1];
1087       // mgmt-gateway lacks HA for now
1088       this.serviceForm.get('count').disable();
1089     } else {
1090       this.serviceForm.get('count').enable();
1091     }
1092   }
1093
1094   onPlacementChange(selected: string) {
1095     if (selected === 'label') {
1096       this.serviceForm.get('count').setValue(null);
1097     }
1098   }
1099
1100   disableForEditing(serviceType: string) {
1101     const disableForEditKeys = ['service_type', 'service_id'];
1102     disableForEditKeys.forEach((key) => {
1103       this.serviceForm.get(key).disable();
1104     });
1105     switch (serviceType) {
1106       case 'ingress':
1107         this.serviceForm.get('backend_service').disable();
1108         break;
1109       case 'nvmeof':
1110         this.serviceForm.get('pool').disable();
1111         this.serviceForm.get('group').disable();
1112         break;
1113     }
1114   }
1115
1116   searchLabels = (text$: Observable<string>) => {
1117     return merge(
1118       text$.pipe(debounceTime(200), distinctUntilChanged()),
1119       this.labelFocus,
1120       this.labelClick.pipe(filter(() => !this.typeahead.isPopupOpen()))
1121     ).pipe(
1122       map((value) =>
1123         this.labels
1124           .filter((label: string) => label.toLowerCase().indexOf(value.toLowerCase()) > -1)
1125           .slice(0, 10)
1126       )
1127     );
1128   };
1129
1130   fileUpload(files: FileList, controlName: string) {
1131     const file: File = files[0];
1132     const reader = new FileReader();
1133     reader.addEventListener('load', (event: ProgressEvent<FileReader>) => {
1134       const control: AbstractControl = this.serviceForm.get(controlName);
1135       control.setValue(event.target.result);
1136       control.markAsDirty();
1137       control.markAsTouched();
1138       control.updateValueAndValidity();
1139     });
1140     reader.readAsText(file, 'utf8');
1141   }
1142
1143   prePopulateId() {
1144     const control: AbstractControl = this.serviceForm.get('service_id');
1145     const backendService = this.serviceForm.getValue('backend_service');
1146     // Set Id as read-only
1147     control.reset({ value: backendService, disabled: true });
1148   }
1149
1150   onSubmit() {
1151     const self = this;
1152     const values: object = this.serviceForm.getRawValue();
1153     const serviceType: string = values['service_type'];
1154     let taskUrl = `service/${URLVerbs.CREATE}`;
1155     if (this.editing) {
1156       taskUrl = `service/${URLVerbs.EDIT}`;
1157     }
1158     const serviceSpec: object = {
1159       service_type: serviceType,
1160       placement: {},
1161       unmanaged: values['unmanaged']
1162     };
1163     if (serviceType === 'rgw') {
1164       serviceSpec['rgw_realm'] = values['realm_name'] ? values['realm_name'] : null;
1165       serviceSpec['rgw_zonegroup'] =
1166         values['zonegroup_name'] !== 'default' ? values['zonegroup_name'] : null;
1167       serviceSpec['rgw_zone'] = values['zone_name'] !== 'default' ? values['zone_name'] : null;
1168     }
1169
1170     const serviceId: string = values['service_id'];
1171     let serviceName: string = serviceType;
1172     if (_.isString(serviceId) && !_.isEmpty(serviceId) && serviceId !== serviceType) {
1173       serviceName = `${serviceType}.${serviceId}`;
1174       serviceSpec['service_id'] = serviceId;
1175     }
1176
1177     // These services has some fields to be
1178     // filled out even if unmanaged is true
1179     switch (serviceType) {
1180       case 'ingress':
1181         serviceSpec['backend_service'] = values['backend_service'];
1182         serviceSpec['service_id'] = values['backend_service'];
1183         if (_.isNumber(values['frontend_port']) && values['frontend_port'] > 0) {
1184           serviceSpec['frontend_port'] = values['frontend_port'];
1185         }
1186         if (_.isString(values['virtual_ip']) && !_.isEmpty(values['virtual_ip'])) {
1187           serviceSpec['virtual_ip'] = values['virtual_ip'].trim();
1188         }
1189         if (_.isNumber(values['monitor_port']) && values['monitor_port'] > 0) {
1190           serviceSpec['monitor_port'] = values['monitor_port'];
1191         }
1192         break;
1193
1194       case 'nvmeof':
1195         serviceSpec['pool'] = values['pool'];
1196         serviceSpec['group'] = values['group'];
1197         serviceSpec['enable_auth'] = values['enable_mtls'];
1198         if (values['enable_mtls']) {
1199           serviceSpec['root_ca_cert'] = values['root_ca_cert'];
1200           serviceSpec['client_cert'] = values['client_cert'];
1201           serviceSpec['client_key'] = values['client_key'];
1202           serviceSpec['server_cert'] = values['server_cert'];
1203           serviceSpec['server_key'] = values['server_key'];
1204         }
1205         break;
1206       case 'iscsi':
1207         serviceSpec['pool'] = values['pool'];
1208         break;
1209
1210       case 'smb':
1211         serviceSpec['cluster_id'] = values['cluster_id']?.trim();
1212         serviceSpec['config_uri'] = values['config_uri']?.trim();
1213         for (const feature in values['features']) {
1214           if (values['features'][feature]) {
1215             (serviceSpec['features'] = serviceSpec['features'] || []).push(feature);
1216           }
1217         }
1218         serviceSpec['custom_dns'] = values['custom_dns']?.trim();
1219         serviceSpec['join_sources'] = values['join_sources']?.trim();
1220         serviceSpec['user_sources'] = values['user_sources']?.trim();
1221         serviceSpec['include_ceph_users'] = values['include_ceph_users']?.trim();
1222         break;
1223
1224       case 'snmp-gateway':
1225         serviceSpec['credentials'] = {};
1226         serviceSpec['snmp_version'] = values['snmp_version'];
1227         serviceSpec['snmp_destination'] = values['snmp_destination'];
1228         if (values['snmp_version'] === 'V3') {
1229           serviceSpec['engine_id'] = values['engine_id'];
1230           serviceSpec['auth_protocol'] = values['auth_protocol'];
1231           serviceSpec['credentials']['snmp_v3_auth_username'] = values['snmp_v3_auth_username'];
1232           serviceSpec['credentials']['snmp_v3_auth_password'] = values['snmp_v3_auth_password'];
1233           if (values['privacy_protocol'] !== null) {
1234             serviceSpec['privacy_protocol'] = values['privacy_protocol'];
1235             serviceSpec['credentials']['snmp_v3_priv_password'] = values['snmp_v3_priv_password'];
1236           }
1237         } else {
1238           serviceSpec['credentials']['snmp_community'] = values['snmp_community'];
1239         }
1240         break;
1241     }
1242
1243     if (!values['unmanaged']) {
1244       switch (values['placement']) {
1245         case 'hosts':
1246           if (values['hosts'].length > 0) {
1247             serviceSpec['placement']['hosts'] = values['hosts'];
1248           }
1249           break;
1250         case 'label':
1251           serviceSpec['placement']['label'] = values['label'];
1252           break;
1253       }
1254       if (_.isNumber(values['count']) && values['count'] > 0) {
1255         serviceSpec['placement']['count'] = values['count'];
1256       }
1257       switch (serviceType) {
1258         case 'rgw':
1259           if (_.isNumber(values['rgw_frontend_port']) && values['rgw_frontend_port'] > 0) {
1260             serviceSpec['rgw_frontend_port'] = values['rgw_frontend_port'];
1261           }
1262           serviceSpec['ssl'] = values['ssl'];
1263           if (values['ssl']) {
1264             serviceSpec['rgw_frontend_ssl_certificate'] = values['ssl_cert']?.trim();
1265           }
1266           break;
1267         case 'iscsi':
1268           if (_.isString(values['trusted_ip_list']) && !_.isEmpty(values['trusted_ip_list'])) {
1269             serviceSpec['trusted_ip_list'] = values['trusted_ip_list'].trim();
1270           }
1271           if (_.isNumber(values['api_port']) && values['api_port'] > 0) {
1272             serviceSpec['api_port'] = values['api_port'];
1273           }
1274           serviceSpec['api_user'] = values['api_user'];
1275           serviceSpec['api_password'] = values['api_password'];
1276           serviceSpec['api_secure'] = values['ssl'];
1277           if (values['ssl']) {
1278             serviceSpec['ssl_cert'] = values['ssl_cert']?.trim();
1279             serviceSpec['ssl_key'] = values['ssl_key']?.trim();
1280           }
1281           break;
1282         case 'ingress':
1283           serviceSpec['ssl'] = values['ssl'];
1284           if (values['ssl']) {
1285             serviceSpec['ssl_cert'] = values['ssl_cert']?.trim();
1286             serviceSpec['ssl_key'] = values['ssl_key']?.trim();
1287           }
1288           serviceSpec['virtual_interface_networks'] = values['virtual_interface_networks'];
1289           break;
1290         case 'mgmt-gateway':
1291           serviceSpec['ssl_cert'] = values['ssl_cert']?.trim();
1292           serviceSpec['ssl_key'] = values['ssl_key']?.trim();
1293           serviceSpec['enable_auth'] = values['enable_auth'];
1294           serviceSpec['port'] = values['port'];
1295           if (serviceSpec['port'] === (443 || 80)) {
1296             // omit port default values due to issues with redirect_url on the backend
1297             delete serviceSpec['port'];
1298           }
1299           serviceSpec['ssl_protocols'] = [];
1300           if (values['ssl_protocols'] != this.DEFAULT_SSL_PROTOCOL_ITEM) {
1301             for (const key of Object.keys(values['ssl_protocols'])) {
1302               serviceSpec['ssl_protocols'].push(values['ssl_protocols'][key]['content']);
1303             }
1304           }
1305           serviceSpec['ssl_ciphers'] = values['ssl_ciphers']?.trim().split(':');
1306           break;
1307         case 'grafana':
1308           serviceSpec['port'] = values['grafana_port'];
1309           serviceSpec['initial_admin_password'] = values['grafana_admin_password'];
1310           break;
1311         case 'oauth2-proxy':
1312           serviceSpec['provider_display_name'] = values['provider_display_name']?.trim();
1313           serviceSpec['client_id'] = values['client_id']?.trim();
1314           serviceSpec['client_secret'] = values['client_secret']?.trim();
1315           serviceSpec['oidc_issuer_url'] = values['oidc_issuer_url']?.trim();
1316           serviceSpec['https_address'] = values['https_address']?.trim();
1317           serviceSpec['redirect_url'] = values['redirect_url']?.trim();
1318           serviceSpec['allowlist_domains'] = values['allowlist_domains']
1319             .split(',')
1320             .map((domain: string) => {
1321               return domain.trim();
1322             });
1323           if (values['ssl']) {
1324             serviceSpec['ssl_cert'] = values['ssl_cert']?.trim();
1325             serviceSpec['ssl_key'] = values['ssl_key']?.trim();
1326           }
1327           break;
1328       }
1329     }
1330     this.taskWrapperService
1331       .wrapTaskAroundCall({
1332         task: new FinishedTask(taskUrl, {
1333           service_name: serviceName
1334         }),
1335         call: this.editing
1336           ? this.cephServiceService.update(serviceSpec)
1337           : this.cephServiceService.create(serviceSpec)
1338       })
1339       .subscribe({
1340         error() {
1341           self.serviceForm.setErrors({ cdSubmitButton: true });
1342         },
1343         complete: () => {
1344           this.pageURL === 'services'
1345             ? this.router.navigate([this.pageURL, { outlets: { modal: null } }])
1346             : this.activeModal.close();
1347         }
1348       });
1349   }
1350
1351   clearValidations() {
1352     const snmpVersion = this.serviceForm.getValue('snmp_version');
1353     const privacyProtocol = this.serviceForm.getValue('privacy_protocol');
1354     if (snmpVersion === 'V3') {
1355       this.serviceForm.get('snmp_community').clearValidators();
1356     } else {
1357       this.serviceForm.get('engine_id').clearValidators();
1358       this.serviceForm.get('auth_protocol').clearValidators();
1359       this.serviceForm.get('privacy_protocol').clearValidators();
1360       this.serviceForm.get('snmp_v3_auth_username').clearValidators();
1361       this.serviceForm.get('snmp_v3_auth_password').clearValidators();
1362     }
1363     if (privacyProtocol === null) {
1364       this.serviceForm.get('snmp_v3_priv_password').clearValidators();
1365     }
1366   }
1367
1368   createMultisiteSetup() {
1369     this.bsModalRef = this.modalService.show(CreateRgwServiceEntitiesComponent, {
1370       size: 'lg'
1371     });
1372     this.bsModalRef.componentInstance.submitAction.subscribe(() => {
1373       this.setRgwFields();
1374     });
1375   }
1376 }