]> git.apps.os.sepia.ceph.com Git - ceph.git/blob
87b94e5e832147d1ea8d87e88e6ca18cf26a4cdb
[ceph.git] /
1 import { Component, OnInit } from '@angular/core';
2 import { FormControl, FormGroup, ValidatorFn, Validators } from '@angular/forms';
3 import { ActivatedRoute, Router } from '@angular/router';
4
5 import { I18n } from '@ngx-translate/i18n-polyfill';
6 import * as _ from 'lodash';
7
8 import { ConfigurationService } from '../../../../shared/api/configuration.service';
9 import { NotificationType } from '../../../../shared/enum/notification-type.enum';
10 import { CdFormGroup } from '../../../../shared/forms/cd-form-group';
11 import { CdValidators } from '../../../../shared/forms/cd-validators';
12 import { NotificationService } from '../../../../shared/services/notification.service';
13 import { ConfigFormCreateRequestModel } from './configuration-form-create-request.model';
14 import { ConfigFormModel } from './configuration-form.model';
15
16 @Component({
17   selector: 'cd-configuration-form',
18   templateUrl: './configuration-form.component.html',
19   styleUrls: ['./configuration-form.component.scss']
20 })
21 export class ConfigurationFormComponent implements OnInit {
22   configForm: CdFormGroup;
23   response: ConfigFormModel;
24   type: string;
25   inputType: string;
26   humanReadableType: string;
27   minValue: number;
28   maxValue: number;
29   patternHelpText: string;
30   availSections = ['global', 'mon', 'mgr', 'osd', 'mds', 'client'];
31
32   constructor(
33     private route: ActivatedRoute,
34     private router: Router,
35     private configService: ConfigurationService,
36     private notificationService: NotificationService,
37     private i18n: I18n
38   ) {
39     this.createForm();
40   }
41
42   createForm() {
43     const formControls = {
44       name: new FormControl({ value: null }),
45       desc: new FormControl({ value: null }),
46       long_desc: new FormControl({ value: null }),
47       values: new FormGroup({}),
48       default: new FormControl({ value: null }),
49       daemon_default: new FormControl({ value: null }),
50       services: new FormControl([])
51     };
52
53     this.availSections.forEach((section) => {
54       formControls.values.addControl(section, new FormControl(null));
55     });
56
57     this.configForm = new CdFormGroup(formControls);
58   }
59
60   ngOnInit() {
61     this.route.params.subscribe((params: { name: string }) => {
62       const configName = params.name;
63       this.configService.get(configName).subscribe((resp: ConfigFormModel) => {
64         this.setResponse(resp);
65       });
66     });
67   }
68
69   getType(type: string): any {
70     const knownTypes = [
71       {
72         name: 'uint',
73         inputType: 'number',
74         humanReadable: this.i18n('Positive integer value'),
75         defaultMin: 0,
76         patternHelpText: this.i18n('The entered value needs to be a positive number.'),
77         isNumberType: true,
78         allowsNegative: false
79       },
80       {
81         name: 'int',
82         inputType: 'number',
83         humanReadable: this.i18n('Integer value'),
84         patternHelpText: this.i18n('The entered value needs to be a number.'),
85         isNumberType: true,
86         allowsNegative: true
87       },
88       {
89         name: 'size',
90         inputType: 'number',
91         humanReadable: this.i18n('Positive integer value (size)'),
92         defaultMin: 0,
93         patternHelpText: this.i18n('The entered value needs to be a positive number.'),
94         isNumberType: true,
95         allowsNegative: false
96       },
97       {
98         name: 'secs',
99         inputType: 'number',
100         humanReadable: this.i18n('Positive integer value (secs)'),
101         defaultMin: 1,
102         patternHelpText: this.i18n('The entered value needs to be a positive number.'),
103         isNumberType: true,
104         allowsNegative: false
105       },
106       {
107         name: 'float',
108         inputType: 'number',
109         humanReadable: this.i18n('Decimal value'),
110         patternHelpText: this.i18n('The entered value needs to be a number or decimal.'),
111         isNumberType: true,
112         allowsNegative: true
113       },
114       {
115         name: 'str',
116         inputType: 'text',
117         humanReadable: this.i18n('Text'),
118         isNumberType: false
119       },
120       {
121         name: 'addr',
122         inputType: 'text',
123         humanReadable: this.i18n('IPv4 or IPv6 address'),
124         patternHelpText: this.i18n('The entered value needs to be a valid IP address.'),
125         isNumberType: false
126       },
127       {
128         name: 'uuid',
129         inputType: 'text',
130         humanReadable: this.i18n('UUID'),
131         patternHelpText: this.i18n(
132           'The entered value is not a valid UUID, e.g.: 67dcac9f-2c03-4d6c-b7bd-1210b3a259a8'
133         ),
134         isNumberType: false
135       },
136       {
137         name: 'bool',
138         inputType: 'checkbox',
139         humanReadable: this.i18n('Boolean value'),
140         isNumberType: false
141       }
142     ];
143
144     let currentType = null;
145
146     knownTypes.forEach((knownType) => {
147       if (knownType.name === type) {
148         currentType = knownType;
149       }
150     });
151
152     if (currentType !== null) {
153       return currentType;
154     }
155
156     throw new Error('Found unknown type "' + type + '" for config option.');
157   }
158
159   getValidators(configOption: any): ValidatorFn[] {
160     const typeParams = this.getType(configOption.type);
161     this.patternHelpText = typeParams.patternHelpText;
162
163     if (typeParams.isNumberType) {
164       const validators = [];
165
166       if (configOption.max && configOption.max !== '') {
167         this.maxValue = configOption.max;
168         validators.push(Validators.max(configOption.max));
169       }
170
171       if ('min' in configOption && configOption.min !== '') {
172         this.minValue = configOption.min;
173         validators.push(Validators.min(configOption.min));
174       } else if ('defaultMin' in typeParams) {
175         this.minValue = typeParams.defaultMin;
176         validators.push(Validators.min(typeParams.defaultMin));
177       }
178
179       if (configOption.type === 'float') {
180         validators.push(CdValidators.decimalNumber());
181       } else {
182         validators.push(CdValidators.number(typeParams.allowsNegative));
183       }
184
185       return validators;
186     } else if (configOption.type === 'addr') {
187       return [CdValidators.ip()];
188     } else if (configOption.type === 'uuid') {
189       return [CdValidators.uuid()];
190     }
191   }
192
193   getStep(type: string, value: number): number | undefined {
194     const numberTypes = ['uint', 'int', 'size', 'secs'];
195
196     if (numberTypes.includes(type)) {
197       return 1;
198     }
199
200     if (type === 'float') {
201       if (value !== null) {
202         const stringVal = value.toString();
203         if (stringVal.indexOf('.') !== -1) {
204           // Value type float and contains decimal characters
205           const decimal = value.toString().split('.');
206           return Math.pow(10, -decimal[1].length);
207         }
208       }
209
210       return 0.1;
211     }
212
213     return undefined;
214   }
215
216   setResponse(response: ConfigFormModel) {
217     this.response = response;
218     const validators = this.getValidators(response);
219
220     this.configForm.get('name').setValue(response.name);
221     this.configForm.get('desc').setValue(response.desc);
222     this.configForm.get('long_desc').setValue(response.long_desc);
223     this.configForm.get('default').setValue(response.default);
224     this.configForm.get('daemon_default').setValue(response.daemon_default);
225     this.configForm.get('services').setValue(response.services);
226
227     if (this.response.value) {
228       this.response.value.forEach((value) => {
229         // Check value type. If it's a boolean value we need to convert it because otherwise we
230         // would use the string representation. That would cause issues for e.g. checkboxes.
231         let sectionValue = null;
232         if (value.value === 'true') {
233           sectionValue = true;
234         } else if (value.value === 'false') {
235           sectionValue = false;
236         } else {
237           sectionValue = value.value;
238         }
239         this.configForm
240           .get('values')
241           .get(value.section)
242           .setValue(sectionValue);
243       });
244     }
245
246     this.availSections.forEach((section) => {
247       this.configForm
248         .get('values')
249         .get(section)
250         .setValidators(validators);
251     });
252
253     const currentType = this.getType(response.type);
254     this.type = currentType.name;
255     this.inputType = currentType.inputType;
256     this.humanReadableType = currentType.humanReadable;
257   }
258
259   createRequest(): ConfigFormCreateRequestModel | null {
260     const values = [];
261
262     this.availSections.forEach((section) => {
263       const sectionValue = this.configForm.getValue(section);
264       if (sectionValue) {
265         values.push({ section: section, value: sectionValue });
266       }
267     });
268
269     if (!_.isEqual(this.response.value, values)) {
270       const request = new ConfigFormCreateRequestModel();
271       request.name = this.configForm.getValue('name');
272       request.value = values;
273       return request;
274     }
275
276     return null;
277   }
278
279   submit() {
280     const request = this.createRequest();
281
282     if (request) {
283       this.configService.create(request).subscribe(
284         () => {
285           this.notificationService.show(
286             NotificationType.success,
287             this.i18n('Config option {{name}} has been updated.', { name: request.name }),
288             this.i18n('Update config option')
289           );
290           this.router.navigate(['/configuration']);
291         },
292         () => {
293           this.configForm.setErrors({ cdSubmitButton: true });
294         }
295       );
296     }
297
298     this.router.navigate(['/configuration']);
299   }
300 }