]> git.apps.os.sepia.ceph.com Git - ceph-ci.git/blob
5b6d900e7520ab58b43959efb69a19b759d61722
[ceph-ci.git] /
1 import { ChangeDetectorRef, Component, OnInit } from '@angular/core';
2 import { AbstractControl, FormArray, FormControl, FormGroup, Validators } from '@angular/forms';
3 import { NgbActiveModal, NgbDateStruct, NgbTimeStruct } from '@ng-bootstrap/ng-bootstrap';
4 import { uniq } from 'lodash';
5 import { Observable, timer } from 'rxjs';
6 import { map, switchMap } from 'rxjs/operators';
7 import { CephfsSnapshotScheduleService } from '~/app/shared/api/cephfs-snapshot-schedule.service';
8 import { CephfsService } from '~/app/shared/api/cephfs.service';
9 import { ActionLabelsI18n, URLVerbs } from '~/app/shared/constants/app.constants';
10 import { Icons } from '~/app/shared/enum/icons.enum';
11 import { RepeatFrequency } from '~/app/shared/enum/repeat-frequency.enum';
12 import { RetentionFrequency } from '~/app/shared/enum/retention-frequency.enum';
13 import { CdForm } from '~/app/shared/forms/cd-form';
14 import { CdFormGroup } from '~/app/shared/forms/cd-form-group';
15 import { CdTableColumn } from '~/app/shared/models/cd-table-column';
16 import { CephfsDir } from '~/app/shared/models/cephfs-directory-models';
17 import { FinishedTask } from '~/app/shared/models/finished-task';
18 import { RetentionPolicy, SnapshotScheduleFormValue } from '~/app/shared/models/snapshot-schedule';
19 import { TaskWrapperService } from '~/app/shared/services/task-wrapper.service';
20
21 const VALIDATON_TIMER = 300;
22
23 @Component({
24   selector: 'cd-cephfs-snapshotschedule-form',
25   templateUrl: './cephfs-snapshotschedule-form.component.html',
26   styleUrls: ['./cephfs-snapshotschedule-form.component.scss']
27 })
28 export class CephfsSnapshotscheduleFormComponent extends CdForm implements OnInit {
29   fsName!: string;
30   id!: number;
31   isEdit = false;
32   icons = Icons;
33   repeatFrequencies = Object.entries(RepeatFrequency);
34   retentionFrequencies = Object.entries(RetentionFrequency);
35
36   currentTime!: NgbTimeStruct;
37   minDate!: NgbDateStruct;
38
39   snapScheduleForm!: CdFormGroup;
40
41   action!: string;
42   resource!: string;
43
44   columns!: CdTableColumn[];
45   directories$!: Observable<CephfsDir[]>;
46
47   constructor(
48     public activeModal: NgbActiveModal,
49     private actionLabels: ActionLabelsI18n,
50     private cephfsService: CephfsService,
51     private snapScheduleService: CephfsSnapshotScheduleService,
52     private taskWrapper: TaskWrapperService,
53     private cd: ChangeDetectorRef
54   ) {
55     super();
56     this.resource = $localize`Snapshot schedule`;
57
58     const currentDatetime = new Date();
59     this.minDate = {
60       year: currentDatetime.getUTCFullYear(),
61       month: currentDatetime.getUTCMonth() + 1,
62       day: currentDatetime.getUTCDate()
63     };
64     this.currentTime = {
65       hour: currentDatetime.getUTCHours(),
66       minute: currentDatetime.getUTCMinutes(),
67       second: currentDatetime.getUTCSeconds()
68     };
69   }
70
71   ngOnInit(): void {
72     this.action = this.actionLabels.CREATE;
73     this.directories$ = this.cephfsService.lsDir(this.id, '/', 3);
74     this.createForm();
75     this.loadingReady();
76   }
77
78   get retentionPolicies() {
79     return this.snapScheduleForm.get('retentionPolicies') as FormArray;
80   }
81
82   createForm() {
83     this.snapScheduleForm = new CdFormGroup(
84       {
85         directory: new FormControl(undefined, {
86           validators: [Validators.required]
87         }),
88         startDate: new FormControl(this.minDate, {
89           validators: [Validators.required]
90         }),
91         startTime: new FormControl(this.currentTime, {
92           validators: [Validators.required]
93         }),
94         repeatInterval: new FormControl(1, {
95           validators: [Validators.required, Validators.min(1)]
96         }),
97         repeatFrequency: new FormControl(RepeatFrequency.Daily, {
98           validators: [Validators.required]
99         }),
100         retentionPolicies: new FormArray([])
101       },
102       {
103         asyncValidators: [this.validateSchedule(), this.validateRetention()]
104       }
105     );
106   }
107
108   addRetentionPolicy() {
109     this.retentionPolicies.push(
110       new FormGroup({
111         retentionInterval: new FormControl(1),
112         retentionFrequency: new FormControl(RetentionFrequency.Daily)
113       })
114     );
115     this.cd.detectChanges();
116   }
117
118   removeRetentionPolicy(idx: number) {
119     this.retentionPolicies.removeAt(idx);
120     this.cd.detectChanges();
121   }
122
123   parseDatetime(date: NgbDateStruct, time?: NgbTimeStruct): string {
124     return `${date.year}-${date.month}-${date.day}T${time.hour || '00'}:${time.minute || '00'}:${
125       time.second || '00'
126     }`;
127   }
128   parseSchedule(interval: number, frequency: string): string {
129     return `${interval}${frequency}`;
130   }
131
132   parseRetentionPolicies(retentionPolicies: RetentionPolicy[]) {
133     return retentionPolicies
134       ?.filter((r) => r?.retentionInterval !== null && r?.retentionFrequency !== null)
135       ?.map?.((r) => `${r.retentionInterval}-${r.retentionFrequency}`)
136       .join('|');
137   }
138
139   submit() {
140     if (this.snapScheduleForm.invalid) {
141       this.snapScheduleForm.setErrors({ cdSubmitButton: true });
142       return;
143     }
144
145     const values = this.snapScheduleForm.value as SnapshotScheduleFormValue;
146
147     const snapScheduleObj = {
148       fs: this.fsName,
149       path: values.directory,
150       snap_schedule: this.parseSchedule(values.repeatInterval, values.repeatFrequency),
151       start: this.parseDatetime(values.startDate, values.startTime)
152     };
153
154     const retentionPoliciesValues = this.parseRetentionPolicies(values?.retentionPolicies);
155     if (retentionPoliciesValues) {
156       snapScheduleObj['retention_policy'] = retentionPoliciesValues;
157     }
158
159     this.taskWrapper
160       .wrapTaskAroundCall({
161         task: new FinishedTask('cephfs/snapshot/schedule/' + URLVerbs.CREATE, {
162           path: snapScheduleObj.path
163         }),
164         call: this.snapScheduleService.create(snapScheduleObj)
165       })
166       .subscribe({
167         error: () => {
168           this.snapScheduleForm.setErrors({ cdSubmitButton: true });
169         },
170         complete: () => {
171           this.activeModal.close();
172         }
173       });
174   }
175
176   validateSchedule() {
177     return (frm: AbstractControl) => {
178       const directory = frm.get('directory');
179       const repeatFrequency = frm.get('repeatFrequency');
180       const repeatInterval = frm.get('repeatInterval');
181       return timer(VALIDATON_TIMER).pipe(
182         switchMap(() =>
183           this.snapScheduleService
184             .checkScheduleExists(
185               directory?.value,
186               this.fsName,
187               repeatInterval?.value,
188               repeatFrequency?.value
189             )
190             .pipe(
191               map((exists: boolean) => {
192                 if (exists) {
193                   repeatFrequency?.setErrors({ notUnique: true }, { emitEvent: true });
194                 } else {
195                   repeatFrequency?.setErrors(null);
196                 }
197                 return null;
198               })
199             )
200         )
201       );
202     };
203   }
204
205   getFormArrayItem(frm: FormGroup, frmArrayName: string, ctrl: string, idx: number) {
206     return (frm.get(frmArrayName) as FormArray)?.controls?.[idx]?.get?.(ctrl);
207   }
208
209   validateRetention() {
210     return (frm: FormGroup) => {
211       return timer(VALIDATON_TIMER).pipe(
212         switchMap(() => {
213           const retentionList = (frm.get('retentionPolicies') as FormArray).controls?.map(
214             (ctrl) => {
215               return ctrl.get('retentionFrequency').value;
216             }
217           );
218           if (uniq(retentionList)?.length !== retentionList?.length) {
219             this.getFormArrayItem(
220               frm,
221               'retentionPolicies',
222               'retentionFrequency',
223               retentionList.length - 1
224             )?.setErrors?.({
225               notUnique: true
226             });
227             return null;
228           }
229           return this.snapScheduleService
230             .checkRetentionPolicyExists(frm.get('directory').value, this.fsName, retentionList)
231             .pipe(
232               map(({ exists, errorIndex }) => {
233                 if (exists) {
234                   this.getFormArrayItem(
235                     frm,
236                     'retentionPolicies',
237                     'retentionFrequency',
238                     errorIndex
239                   )?.setErrors?.({ notUnique: true });
240                 } else {
241                   (frm.get('retentionPolicies') as FormArray).controls?.forEach?.((_, i) => {
242                     this.getFormArrayItem(
243                       frm,
244                       'retentionPolicies',
245                       'retentionFrequency',
246                       i
247                     )?.setErrors?.(null);
248                   });
249                 }
250                 return null;
251               })
252             );
253         })
254       );
255     };
256   }
257 }