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';
21 const VALIDATON_TIMER = 300;
24 selector: 'cd-cephfs-snapshotschedule-form',
25 templateUrl: './cephfs-snapshotschedule-form.component.html',
26 styleUrls: ['./cephfs-snapshotschedule-form.component.scss']
28 export class CephfsSnapshotscheduleFormComponent extends CdForm implements OnInit {
33 repeatFrequencies = Object.entries(RepeatFrequency);
34 retentionFrequencies = Object.entries(RetentionFrequency);
36 currentTime!: NgbTimeStruct;
37 minDate!: NgbDateStruct;
39 snapScheduleForm!: CdFormGroup;
44 columns!: CdTableColumn[];
45 directories$!: Observable<CephfsDir[]>;
48 public activeModal: NgbActiveModal,
49 private actionLabels: ActionLabelsI18n,
50 private cephfsService: CephfsService,
51 private snapScheduleService: CephfsSnapshotScheduleService,
52 private taskWrapper: TaskWrapperService,
53 private cd: ChangeDetectorRef
56 this.resource = $localize`Snapshot schedule`;
58 const currentDatetime = new Date();
60 year: currentDatetime.getUTCFullYear(),
61 month: currentDatetime.getUTCMonth() + 1,
62 day: currentDatetime.getUTCDate()
65 hour: currentDatetime.getUTCHours(),
66 minute: currentDatetime.getUTCMinutes(),
67 second: currentDatetime.getUTCSeconds()
72 this.action = this.actionLabels.CREATE;
73 this.directories$ = this.cephfsService.lsDir(this.id, '/', 3);
78 get retentionPolicies() {
79 return this.snapScheduleForm.get('retentionPolicies') as FormArray;
83 this.snapScheduleForm = new CdFormGroup(
85 directory: new FormControl(undefined, {
86 validators: [Validators.required]
88 startDate: new FormControl(this.minDate, {
89 validators: [Validators.required]
91 startTime: new FormControl(this.currentTime, {
92 validators: [Validators.required]
94 repeatInterval: new FormControl(1, {
95 validators: [Validators.required, Validators.min(1)]
97 repeatFrequency: new FormControl(RepeatFrequency.Daily, {
98 validators: [Validators.required]
100 retentionPolicies: new FormArray([])
103 asyncValidators: [this.validateSchedule(), this.validateRetention()]
108 addRetentionPolicy() {
109 this.retentionPolicies.push(
111 retentionInterval: new FormControl(1),
112 retentionFrequency: new FormControl(RetentionFrequency.Daily)
115 this.cd.detectChanges();
118 removeRetentionPolicy(idx: number) {
119 this.retentionPolicies.removeAt(idx);
120 this.cd.detectChanges();
123 parseDatetime(date: NgbDateStruct, time?: NgbTimeStruct): string {
124 return `${date.year}-${date.month}-${date.day}T${time.hour || '00'}:${time.minute || '00'}:${
128 parseSchedule(interval: number, frequency: string): string {
129 return `${interval}${frequency}`;
132 parseRetentionPolicies(retentionPolicies: RetentionPolicy[]) {
133 return retentionPolicies
134 ?.filter((r) => r?.retentionInterval !== null && r?.retentionFrequency !== null)
135 ?.map?.((r) => `${r.retentionInterval}-${r.retentionFrequency}`)
140 if (this.snapScheduleForm.invalid) {
141 this.snapScheduleForm.setErrors({ cdSubmitButton: true });
145 const values = this.snapScheduleForm.value as SnapshotScheduleFormValue;
147 const snapScheduleObj = {
149 path: values.directory,
150 snap_schedule: this.parseSchedule(values.repeatInterval, values.repeatFrequency),
151 start: this.parseDatetime(values.startDate, values.startTime)
154 const retentionPoliciesValues = this.parseRetentionPolicies(values?.retentionPolicies);
155 if (retentionPoliciesValues) {
156 snapScheduleObj['retention_policy'] = retentionPoliciesValues;
160 .wrapTaskAroundCall({
161 task: new FinishedTask('cephfs/snapshot/schedule/' + URLVerbs.CREATE, {
162 path: snapScheduleObj.path
164 call: this.snapScheduleService.create(snapScheduleObj)
168 this.snapScheduleForm.setErrors({ cdSubmitButton: true });
171 this.activeModal.close();
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(
183 this.snapScheduleService
184 .checkScheduleExists(
187 repeatInterval?.value,
188 repeatFrequency?.value
191 map((exists: boolean) => {
193 repeatFrequency?.setErrors({ notUnique: true }, { emitEvent: true });
195 repeatFrequency?.setErrors(null);
205 getFormArrayItem(frm: FormGroup, frmArrayName: string, ctrl: string, idx: number) {
206 return (frm.get(frmArrayName) as FormArray)?.controls?.[idx]?.get?.(ctrl);
209 validateRetention() {
210 return (frm: FormGroup) => {
211 return timer(VALIDATON_TIMER).pipe(
213 const retentionList = (frm.get('retentionPolicies') as FormArray).controls?.map(
215 return ctrl.get('retentionFrequency').value;
218 if (uniq(retentionList)?.length !== retentionList?.length) {
219 this.getFormArrayItem(
222 'retentionFrequency',
223 retentionList.length - 1
229 return this.snapScheduleService
230 .checkRetentionPolicyExists(frm.get('directory').value, this.fsName, retentionList)
232 map(({ exists, errorIndex }) => {
234 this.getFormArrayItem(
237 'retentionFrequency',
239 )?.setErrors?.({ notUnique: true });
241 (frm.get('retentionPolicies') as FormArray).controls?.forEach?.((_, i) => {
242 this.getFormArrayItem(
245 'retentionFrequency',
247 )?.setErrors?.(null);