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