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