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