]> git.proxmox.com Git - ceph.git/blob - ceph/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/mirroring/bootstrap-import-modal/bootstrap-import-modal.component.ts
import 15.2.0 Octopus source
[ceph.git] / ceph / src / pybind / mgr / dashboard / frontend / src / app / ceph / block / mirroring / bootstrap-import-modal / bootstrap-import-modal.component.ts
1 import { Component, OnDestroy, OnInit } from '@angular/core';
2 import { FormControl, FormGroup, ValidatorFn, Validators } from '@angular/forms';
3
4 import * as _ from 'lodash';
5 import { BsModalRef } from 'ngx-bootstrap/modal';
6 import { concat, forkJoin, Observable, Subscription } from 'rxjs';
7 import { last } from 'rxjs/operators';
8
9 import { RbdMirroringService } from '../../../../shared/api/rbd-mirroring.service';
10 import { CdFormGroup } from '../../../../shared/forms/cd-form-group';
11 import { FinishedTask } from '../../../../shared/models/finished-task';
12 import { TaskWrapperService } from '../../../../shared/services/task-wrapper.service';
13 import { Pool } from '../../../pool/pool';
14
15 @Component({
16 selector: 'cd-bootstrap-import-modal',
17 templateUrl: './bootstrap-import-modal.component.html',
18 styleUrls: ['./bootstrap-import-modal.component.scss']
19 })
20 export class BootstrapImportModalComponent implements OnInit, OnDestroy {
21 siteName: string;
22 pools: any[] = [];
23 token: string;
24
25 subs: Subscription;
26
27 importBootstrapForm: CdFormGroup;
28
29 directions: Array<any> = [
30 { key: 'rx-tx', desc: 'Bidirectional' },
31 { key: 'rx', desc: 'Unidirectional (receive-only)' }
32 ];
33
34 constructor(
35 public modalRef: BsModalRef,
36 private rbdMirroringService: RbdMirroringService,
37 private taskWrapper: TaskWrapperService
38 ) {
39 this.createForm();
40 }
41
42 createForm() {
43 this.importBootstrapForm = new CdFormGroup({
44 siteName: new FormControl('', {
45 validators: [Validators.required]
46 }),
47 direction: new FormControl('rx-tx', {}),
48 pools: new FormGroup(
49 {},
50 {
51 validators: [this.validatePools()]
52 }
53 ),
54 token: new FormControl('', {
55 validators: [Validators.required, this.validateToken()]
56 })
57 });
58 }
59
60 ngOnInit() {
61 this.rbdMirroringService.getSiteName().subscribe((response: any) => {
62 this.importBootstrapForm.get('siteName').setValue(response.site_name);
63 });
64
65 this.subs = this.rbdMirroringService.subscribeSummary((data: any) => {
66 if (!data) {
67 return;
68 }
69
70 const pools = data.content_data.pools;
71 this.pools = pools.reduce((acc: any[], pool: Pool) => {
72 acc.push({
73 name: pool['name'],
74 mirror_mode: pool['mirror_mode']
75 });
76 return acc;
77 }, []);
78
79 const poolsControl = this.importBootstrapForm.get('pools') as FormGroup;
80 _.each(this.pools, (pool) => {
81 const poolName = pool['name'];
82 const mirroring_disabled = pool['mirror_mode'] === 'disabled';
83 const control = poolsControl.controls[poolName];
84 if (control) {
85 if (mirroring_disabled && control.disabled) {
86 control.enable();
87 } else if (!mirroring_disabled && control.enabled) {
88 control.disable();
89 control.setValue(true);
90 }
91 } else {
92 poolsControl.addControl(
93 poolName,
94 new FormControl({ value: !mirroring_disabled, disabled: !mirroring_disabled })
95 );
96 }
97 });
98 });
99 }
100
101 ngOnDestroy() {
102 if (this.subs) {
103 this.subs.unsubscribe();
104 }
105 }
106
107 validatePools(): ValidatorFn {
108 return (poolsControl: FormGroup): { [key: string]: any } => {
109 let checkedCount = 0;
110 _.each(poolsControl.controls, (control) => {
111 if (control.value === true) {
112 ++checkedCount;
113 }
114 });
115
116 if (checkedCount > 0) {
117 return null;
118 }
119
120 return { requirePool: true };
121 };
122 }
123
124 validateToken(): ValidatorFn {
125 return (token: FormControl): { [key: string]: any } => {
126 try {
127 if (JSON.parse(atob(token.value))) {
128 return null;
129 }
130 } catch (error) {}
131 return { invalidToken: true };
132 };
133 }
134
135 import() {
136 const bootstrapPoolNames: string[] = [];
137 const poolNames: string[] = [];
138 const poolsControl = this.importBootstrapForm.get('pools') as FormGroup;
139 _.each(poolsControl.controls, (control, poolName) => {
140 if (control.value === true) {
141 bootstrapPoolNames.push(poolName);
142 if (!control.disabled) {
143 poolNames.push(poolName);
144 }
145 }
146 });
147
148 const poolModeRequest = {
149 mirror_mode: 'image'
150 };
151
152 let apiActionsObs: Observable<any> = concat(
153 this.rbdMirroringService.setSiteName(this.importBootstrapForm.getValue('siteName')),
154 forkJoin(
155 poolNames.map((poolName) => this.rbdMirroringService.updatePool(poolName, poolModeRequest))
156 )
157 );
158
159 apiActionsObs = bootstrapPoolNames
160 .reduce((obs, poolName) => {
161 return concat(
162 obs,
163 this.rbdMirroringService.importBootstrapToken(
164 poolName,
165 this.importBootstrapForm.getValue('direction'),
166 this.importBootstrapForm.getValue('token')
167 )
168 );
169 }, apiActionsObs)
170 .pipe(last());
171
172 const finishHandler = () => {
173 this.rbdMirroringService.refresh();
174 this.importBootstrapForm.setErrors({ cdSubmitButton: true });
175 };
176
177 const taskObs = this.taskWrapper.wrapTaskAroundCall({
178 task: new FinishedTask('rbd/mirroring/bootstrap/import', {}),
179 call: apiActionsObs
180 });
181 taskObs.subscribe(undefined, finishHandler, () => {
182 finishHandler();
183 this.modalRef.hide();
184 });
185 }
186 }