]> git.apps.os.sepia.ceph.com Git - ceph-ci.git/blob
00e4f002b9a6aca36f8ac14760a7e5d452fdce46
[ceph-ci.git] /
1 import { ChangeDetectorRef, Component, Inject, OnInit, Optional } from '@angular/core';
2 import {
3   AbstractControl,
4   FormArray,
5   FormControl,
6   FormGroup,
7   ValidationErrors,
8   Validators
9 } from '@angular/forms';
10 import { CdFormBuilder } from '~/app/shared/forms/cd-form-builder';
11 import { CdFormGroup } from '~/app/shared/forms/cd-form-group';
12 import { CdValidators } from '~/app/shared/forms/cd-validators';
13 import { Bucket } from '../models/rgw-bucket';
14 import { RgwBucketService } from '~/app/shared/api/rgw-bucket.service';
15 import { NotificationService } from '~/app/shared/services/notification.service';
16 import { NotificationType } from '~/app/shared/enum/notification-type.enum';
17 import { RgwZonegroupService } from '~/app/shared/api/rgw-zonegroup.service';
18 import { BucketTieringUtils } from '../utils/rgw-bucket-tiering';
19 import { StorageClass, ZoneGroupDetails } from '../models/rgw-storage-class.model';
20 import { CdForm } from '~/app/shared/forms/cd-form';
21 import { Router } from '@angular/router';
22 import { ActionLabelsI18n } from '~/app/shared/constants/app.constants';
23
24 export interface Tags {
25   tagKey: number;
26   tagValue: string;
27 }
28
29 @Component({
30   selector: 'cd-rgw-bucket-tiering',
31   templateUrl: './rgw-bucket-tiering-form.component.html',
32   styleUrls: ['./rgw-bucket-tiering-form.component.scss']
33 })
34 export class RgwBucketTieringFormComponent extends CdForm implements OnInit {
35   tieringForm: CdFormGroup;
36   tagsToRemove: Tags[] = [];
37   storageClassList: StorageClass[] = null;
38   configuredLifecycle: any;
39   isStorageClassFetched = false;
40
41   constructor(
42     @Inject('bucket') public bucket: Bucket,
43     @Optional() @Inject('selectedLifecycle') public selectedLifecycle: any,
44     @Optional() @Inject('editing') public editing = false,
45     public actionLabels: ActionLabelsI18n,
46     private rgwBucketService: RgwBucketService,
47     private fb: CdFormBuilder,
48     private cd: ChangeDetectorRef,
49     private rgwZonegroupService: RgwZonegroupService,
50     private notificationService: NotificationService,
51     private router: Router
52   ) {
53     super();
54   }
55
56   ngOnInit() {
57     this.rgwBucketService
58       .getLifecycle(this.bucket.bucket, this.bucket.owner)
59       .subscribe((lifecycle) => {
60         this.configuredLifecycle = lifecycle || { LifecycleConfiguration: { Rules: [] } };
61         if (this.editing) {
62           const ruleToEdit = this.configuredLifecycle?.['LifecycleConfiguration']?.['Rules'].filter(
63             (rule: any) => rule?.['ID'] === this.selectedLifecycle?.['ID']
64           )[0];
65           this.tieringForm.patchValue({
66             name: ruleToEdit?.['ID'],
67             hasPrefix: this.checkIfRuleHasFilters(ruleToEdit),
68             prefix:
69               ruleToEdit?.['Prefix'] ||
70               ruleToEdit?.['Filter']?.['Prefix'] ||
71               ruleToEdit?.['Filter']?.['And']?.['Prefix'] ||
72               '',
73             status: ruleToEdit?.['Status'],
74             days: ruleToEdit?.['Transition']?.['Days']
75           });
76           this.setTags(ruleToEdit);
77           this.tieringForm.get('name').disable();
78         }
79       });
80     this.tieringForm = this.fb.group({
81       name: [null, [Validators.required, this.duplicateConfigName.bind(this)]],
82       storageClass: [null, Validators.required],
83       hasPrefix: [false, [Validators.required]],
84       prefix: [null, [CdValidators.composeIf({ hasPrefix: true }, [Validators.required])]],
85       tags: this.fb.array([]),
86       status: ['Enabled', [Validators.required]],
87       days: [60, [Validators.required, CdValidators.number(false)]]
88     });
89     this.loadStorageClass();
90   }
91
92   checkIfRuleHasFilters(rule: any) {
93     if (
94       this.isValidPrefix(rule?.['Prefix']) ||
95       this.isValidPrefix(rule?.['Filter']?.['Prefix']) ||
96       this.isValidArray(rule?.['Filter']?.['Tags']) ||
97       this.isValidPrefix(rule?.['Filter']?.['And']?.['Prefix']) ||
98       this.isValidArray(rule?.['Filter']?.['And']?.['Tags'])
99     ) {
100       return true;
101     }
102     return false;
103   }
104
105   isValidPrefix(value: string) {
106     return value !== undefined && value !== '';
107   }
108
109   isValidArray(value: object[]) {
110     return Array.isArray(value) && value.length > 0;
111   }
112
113   setTags(rule: any) {
114     if (rule?.['Filter']?.['Tags']?.length > 0) {
115       rule?.['Filter']?.['Tags']?.forEach((tag: { Key: string; Value: string }) =>
116         this.addTags(tag.Key, tag.Value)
117       );
118     }
119     if (rule?.['Filter']?.['And']?.['Tags']?.length > 0) {
120       rule?.['Filter']?.['And']?.['Tags']?.forEach((tag: { Key: string; Value: string }) =>
121         this.addTags(tag.Key, tag.Value)
122       );
123     }
124   }
125
126   get tags() {
127     return this.tieringForm.get('tags') as FormArray;
128   }
129
130   addTags(key?: string, value?: string) {
131     this.tags.push(
132       new FormGroup({
133         Key: new FormControl(key),
134         Value: new FormControl(value)
135       })
136     );
137     this.cd.detectChanges();
138   }
139
140   duplicateConfigName(control: AbstractControl): ValidationErrors | null {
141     if (this.configuredLifecycle?.LifecycleConfiguration?.Rules?.length > 0) {
142       const ruleIds = this.configuredLifecycle.LifecycleConfiguration.Rules.map(
143         (rule: any) => rule.ID
144       );
145       return ruleIds.includes(control.value) ? { duplicate: true } : null;
146     }
147     return null;
148   }
149
150   removeTags(idx: number) {
151     this.tags.removeAt(idx);
152     this.cd.detectChanges();
153   }
154
155   loadStorageClass(): Promise<void> {
156     return new Promise((resolve, reject) => {
157       this.rgwZonegroupService.getAllZonegroupsInfo().subscribe(
158         (data: ZoneGroupDetails) => {
159           this.storageClassList = [];
160           const tierObj = BucketTieringUtils.filterAndMapTierTargets(data);
161           this.isStorageClassFetched = true;
162           this.storageClassList.push(...tierObj);
163           if (this.editing) {
164             this.tieringForm
165               .get('storageClass')
166               .setValue(this.selectedLifecycle?.['Transition']?.['StorageClass']);
167           }
168           this.loadingReady();
169           resolve();
170         },
171         (error) => {
172           reject(error);
173         }
174       );
175     });
176   }
177
178   submitTieringConfig() {
179     const formValue = this.tieringForm.value;
180     if (!this.tieringForm.valid) {
181       return;
182     }
183
184     let lifecycle: any = {
185       ID: this.tieringForm.getRawValue().name,
186       Status: formValue.status,
187       Transition: [
188         {
189           Days: formValue.days,
190           StorageClass: formValue.storageClass
191         }
192       ]
193     };
194     if (formValue.hasPrefix) {
195       if (this.tags.length > 0) {
196         Object.assign(lifecycle, {
197           Filter: {
198             And: {
199               Prefix: formValue.prefix,
200               Tag: this.tags.value
201             }
202           }
203         });
204       } else {
205         Object.assign(lifecycle, {
206           Filter: {
207             Prefix: formValue.prefix
208           }
209         });
210       }
211     } else {
212       Object.assign(lifecycle, {
213         Filter: {}
214       });
215     }
216     if (!this.editing) {
217       this.configuredLifecycle.LifecycleConfiguration.Rules.push(lifecycle);
218       this.rgwBucketService
219         .setLifecycle(
220           this.bucket.bucket,
221           JSON.stringify(this.configuredLifecycle.LifecycleConfiguration),
222           this.bucket.owner
223         )
224         .subscribe({
225           next: () => {
226             this.notificationService.show(
227               NotificationType.success,
228               $localize`Bucket lifecycle created succesfully`
229             );
230           },
231           error: (error: any) => {
232             this.notificationService.show(NotificationType.error, error);
233             this.tieringForm.setErrors({ cdSubmitButton: true });
234           },
235           complete: () => {
236             this.closeModal();
237           }
238         });
239     } else {
240       const rules = this.configuredLifecycle.LifecycleConfiguration.Rules;
241       const index = rules.findIndex((rule: any) => rule?.['ID'] === this.selectedLifecycle?.['ID']);
242       rules.splice(index, 1, lifecycle);
243       this.rgwBucketService
244         .setLifecycle(
245           this.bucket.bucket,
246           JSON.stringify(this.configuredLifecycle.LifecycleConfiguration),
247           this.bucket.owner
248         )
249         .subscribe({
250           next: () => {
251             this.notificationService.show(
252               NotificationType.success,
253               $localize`Bucket lifecycle modified succesfully`
254             );
255           },
256           error: (error: any) => {
257             this.notificationService.show(NotificationType.error, error);
258             this.tieringForm.setErrors({ cdSubmitButton: true });
259           },
260           complete: () => {
261             this.closeModal();
262           }
263         });
264     }
265   }
266
267   goToCreateStorageClass() {
268     this.router.navigate(['rgw/tiering/create']);
269   }
270 }