1 import { Component, OnInit } from '@angular/core';
2 import { AbstractControl, FormControl, Validators } from '@angular/forms';
3 import { ActionLabelsI18n, URLVerbs } from '~/app/shared/constants/app.constants';
4 import { CdForm } from '~/app/shared/forms/cd-form';
5 import { CdFormBuilder } from '~/app/shared/forms/cd-form-builder';
6 import { CdFormGroup } from '~/app/shared/forms/cd-form-group';
7 import _ from 'lodash';
8 import { ActivatedRoute, Router } from '@angular/router';
9 import { RgwStorageClassService } from '~/app/shared/api/rgw-storage-class.service';
10 import { RgwZonegroupService } from '~/app/shared/api/rgw-zonegroup.service';
12 ALLOW_READ_THROUGH_TEXT,
14 MULTIPART_MIN_PART_TEXT,
15 MULTIPART_SYNC_THRESHOLD_TEXT,
18 RETAIN_HEAD_OBJECT_TEXT,
21 TARGET_ACCESS_KEY_TEXT,
25 TARGET_SECRET_KEY_TEXT,
30 CLOUDS3_STORAGE_CLASS_TEXT,
31 LOCAL_STORAGE_CLASS_TEXT,
32 GLACIER_STORAGE_CLASS_TEXT,
33 GLACIER_RESTORE_DAY_TEXT,
34 GLACIER_RESTORE_TIER_TYPE_TEXT,
36 READTHROUGH_RESTORE_DAYS_TEXT,
37 RESTORE_STORAGE_CLASS_TEXT,
41 STORAGE_CLASS_CONSTANTS,
42 STANDARD_TIER_TYPE_TEXT,
43 EXPEDITED_TIER_TYPE_TEXT,
45 } from '../models/rgw-storage-class.model';
46 import { NotificationType } from '~/app/shared/enum/notification-type.enum';
47 import { NotificationService } from '~/app/shared/services/notification.service';
48 import { CdValidators } from '~/app/shared/forms/cd-validators';
51 selector: 'cd-rgw-storage-class-form',
52 templateUrl: './rgw-storage-class-form.component.html',
53 styleUrls: ['./rgw-storage-class-form.component.scss']
55 export class RgwStorageClassFormComponent extends CdForm implements OnInit {
56 storageClassForm: CdFormGroup;
60 showAdvanced: boolean = false;
61 defaultZoneGroup: string;
62 zonegroupNames: ZoneGroup[];
63 placementTargets: string[] = [];
64 selectedZoneGroup: string;
65 defaultZonegroup: ZoneGroup;
66 zoneGroupDetails: ZoneGroupDetails;
67 storageClassInfo: StorageClass;
68 tierTargetInfo: TierTarget;
69 glacierStorageClassDetails: S3Glacier;
70 allowReadThrough: boolean = false;
71 TIER_TYPE = TIER_TYPE;
72 TIER_TYPE_DISPLAY = TIER_TYPE_DISPLAY;
73 storageClassOptions: StorageClassOption[];
74 textLabels: TextLabels;
77 public actionLabels: ActionLabelsI18n,
78 private formBuilder: CdFormBuilder,
79 private notificationService: NotificationService,
80 private rgwStorageService: RgwStorageClassService,
81 private rgwZoneGroupService: RgwZonegroupService,
82 private router: Router,
83 private route: ActivatedRoute
86 this.resource = $localize`Tiering Storage Class`;
87 this.editing = this.router.url.startsWith(`/rgw/tiering/${URLVerbs.EDIT}`);
88 this.action = this.editing ? this.actionLabels.EDIT : this.actionLabels.CREATE;
93 targetPathText: TARGET_PATH_TEXT,
94 targetEndpointText: TARGET_ENDPOINT_TEXT,
95 targetRegionText: TARGET_REGION_TEXT,
96 targetAccessKeyText: TARGET_ACCESS_KEY_TEXT,
97 targetSecretKeyText: TARGET_SECRET_KEY_TEXT,
98 retainHeadObjectText: RETAIN_HEAD_OBJECT_TEXT,
99 allowReadThroughText: ALLOW_READ_THROUGH_TEXT,
100 storageClassText: LOCAL_STORAGE_CLASS_TEXT,
101 multipartMinPartText: MULTIPART_MIN_PART_TEXT,
102 multipartSyncThresholdText: MULTIPART_SYNC_THRESHOLD_TEXT,
103 tiertypeText: STANDARD_TIER_TYPE_TEXT,
104 glacierRestoreDayText: GLACIER_RESTORE_DAY_TEXT,
105 glacierRestoreTiertypeText: GLACIER_RESTORE_TIER_TYPE_TEXT,
106 restoreDaysText: RESTORE_DAYS_TEXT,
107 readthroughrestoreDaysText: READTHROUGH_RESTORE_DAYS_TEXT,
108 restoreStorageClassText: RESTORE_STORAGE_CLASS_TEXT
110 this.storageClassOptions = [
111 { value: TIER_TYPE.LOCAL, label: TIER_TYPE_DISPLAY.LOCAL },
112 { value: TIER_TYPE.CLOUD_TIER, label: TIER_TYPE_DISPLAY.CLOUD_TIER },
113 { value: TIER_TYPE.GLACIER, label: TIER_TYPE_DISPLAY.GLACIER }
116 this.storageClassTypeText();
119 this.loadZoneGroup();
121 this.route.params.subscribe((params: StorageClass) => {
122 this.storageClassInfo = params;
124 this.rgwStorageService
125 .getPlacement_target(this.storageClassInfo.placement_target)
126 .subscribe((placementTargetInfo: PlacementTarget) => {
127 this.tierTargetInfo = this.getTierTargetByStorageClass(
129 this.storageClassInfo.storage_class
131 let response = this.tierTargetInfo?.val?.s3;
132 this.storageClassForm.get('zonegroup').disable();
133 this.storageClassForm.get('placement_target').disable();
134 this.storageClassForm.get('storage_class').disable();
135 this.storageClassForm.patchValue({
136 zonegroup: this.storageClassInfo?.zonegroup_name,
137 region: response?.region,
138 placement_target: this.storageClassInfo?.placement_target,
139 storageClassType: this.tierTargetInfo?.val?.tier_type ?? TIER_TYPE.LOCAL,
140 endpoint: response?.endpoint,
141 storage_class: this.storageClassInfo?.storage_class,
142 access_key: response?.access_key,
143 secret_key: response?.secret,
144 target_path: response?.target_path,
145 retain_head_object: this.tierTargetInfo?.val?.retain_head_object || false,
146 multipart_sync_threshold: response?.multipart_sync_threshold || '',
147 multipart_min_part_size: response?.multipart_min_part_size || '',
148 allow_read_through: this.tierTargetInfo?.val?.allow_read_through || false,
149 restore_storage_class: this.tierTargetInfo?.val?.restore_storage_class,
150 read_through_restore_days: this.tierTargetInfo?.val?.read_through_restore_days
152 if (this.tierTargetInfo?.val?.tier_type == TIER_TYPE.GLACIER) {
153 let glacierResponse = this.tierTargetInfo?.val['s3-glacier'];
154 this.storageClassForm.patchValue({
155 glacier_restore_tier_type: glacierResponse.glacier_restore_tier_type,
156 glacier_restore_days: glacierResponse.glacier_restore_days
161 this.storageClassForm.get('storageClassType').valueChanges.subscribe((value) => {
162 this.updateValidatorsBasedOnStorageClass(value);
164 this.storageClassForm.get('allow_read_through').valueChanges.subscribe((value) => {
165 this.onAllowReadThroughChange(value);
169 private updateValidatorsBasedOnStorageClass(value: string) {
170 const controlsToUpdate = [
176 'glacier_restore_tier_type',
177 'restore_storage_class'
180 controlsToUpdate.forEach((field) => {
181 const control = this.storageClassForm.get(field);
184 (value === TIER_TYPE.CLOUD_TIER &&
185 ['region', 'endpoint', 'access_key', 'secret_key', 'target_path'].includes(field)) ||
186 (value === TIER_TYPE.GLACIER &&
188 'glacier_restore_tier_type',
189 'restore_storage_class',
197 control.setValidators([Validators.required]);
199 control.clearValidators();
202 control.updateValueAndValidity();
206 storageClassTypeText() {
207 this.storageClassForm?.get('storageClassType')?.valueChanges.subscribe((value) => {
208 if (value === TIER_TYPE.LOCAL) {
209 this.textLabels.storageClassText = LOCAL_STORAGE_CLASS_TEXT;
210 } else if (value === TIER_TYPE.CLOUD_TIER) {
211 this.textLabels.storageClassText = CLOUDS3_STORAGE_CLASS_TEXT;
212 } else if (value === TIER_TYPE.GLACIER) {
213 this.textLabels.storageClassText = GLACIER_STORAGE_CLASS_TEXT;
219 this.storageClassForm?.get('glacier_restore_tier_type')?.valueChanges.subscribe((value) => {
220 if (value === STORAGE_CLASS_CONSTANTS.DEFAULT_STORAGE_CLASS) {
221 this.textLabels.tiertypeText = STANDARD_TIER_TYPE_TEXT;
223 this.textLabels.tiertypeText = EXPEDITED_TIER_TYPE_TEXT;
231 const lockDaysValidator = CdValidators.custom('lockDays', () => {
232 if (!self.storageClassForm || !self.storageClassForm.getRawValue()) {
236 const lockDays = Number(self.storageClassForm.getValue('read_through_restore_days'));
237 return !Number.isInteger(lockDays) || lockDays === 0;
239 this.storageClassForm = this.formBuilder.group({
240 storage_class: new FormControl('', {
241 validators: [Validators.required]
243 zonegroup: new FormControl(this.selectedZoneGroup, {
244 validators: [Validators.required]
246 region: new FormControl('', [
247 CdValidators.composeIf({ storageClassType: TIER_TYPE.CLOUD_TIER }, [Validators.required])
249 placement_target: new FormControl('', {
250 validators: [Validators.required]
252 endpoint: new FormControl(null, [
253 CdValidators.composeIf({ storageClassType: TIER_TYPE.CLOUD_TIER }, [Validators.required])
255 access_key: new FormControl(null, [
256 CdValidators.composeIf({ storageClassType: TIER_TYPE.CLOUD_TIER }, [Validators.required])
258 secret_key: new FormControl(null, [
259 CdValidators.composeIf({ storageClassType: TIER_TYPE.CLOUD_TIER }, [Validators.required])
261 target_path: new FormControl('', [
262 CdValidators.composeIf({ storageClassType: TIER_TYPE.CLOUD_TIER }, [Validators.required])
264 retain_head_object: new FormControl(true),
265 glacier_restore_tier_type: new FormControl(STORAGE_CLASS_CONSTANTS.DEFAULT_STORAGE_CLASS, [
266 CdValidators.composeIf({ storageClassType: TIER_TYPE.GLACIER }, [Validators.required])
268 glacier_restore_days: new FormControl(STORAGE_CLASS_CONSTANTS.DEFAULT_GLACIER_RESTORE_DAYS, [
269 CdValidators.composeIf({ storageClassType: TIER_TYPE.GLACIER || TIER_TYPE.CLOUD_TIER }, [
270 CdValidators.number(false),
274 restore_storage_class: new FormControl(STORAGE_CLASS_CONSTANTS.DEFAULT_STORAGE_CLASS),
275 read_through_restore_days: new FormControl(
277 value: STORAGE_CLASS_CONSTANTS.DEFAULT_READTHROUGH_RESTORE_DAYS,
280 CdValidators.composeIf(
281 (form: AbstractControl) => {
282 const type = form.get('storageClassType')?.value;
283 return type === TIER_TYPE.GLACIER || type === TIER_TYPE.CLOUD_TIER;
285 [CdValidators.number(false), lockDaysValidator]
288 multipart_sync_threshold: new FormControl(
289 STORAGE_CLASS_CONSTANTS.DEFAULT_MULTIPART_SYNC_THRESHOLD
291 multipart_min_part_size: new FormControl(
292 STORAGE_CLASS_CONSTANTS.DEFAULT_MULTIPART_MIN_PART_SIZE
294 allow_read_through: new FormControl(false),
295 storageClassType: new FormControl(TIER_TYPE.LOCAL, Validators.required)
299 loadZoneGroup(): Promise<void> {
300 return new Promise((resolve, reject) => {
301 this.rgwZoneGroupService.getAllZonegroupsInfo().subscribe(
302 (data: ZoneGroupDetails) => {
303 this.zoneGroupDetails = data;
304 this.zonegroupNames = [];
305 this.placementTargets = [];
306 if (data.zonegroups && data.zonegroups.length > 0) {
307 this.zonegroupNames = data.zonegroups.map((zoneGroup: ZoneGroup) => {
314 this.defaultZonegroup = this.zonegroupNames.find(
315 (zonegroups: ZoneGroup) => zonegroups.id === data.default_zonegroup
317 this.storageClassForm.get('zonegroup').setValue(this.defaultZonegroup.name);
318 this.onZonegroupChange();
321 (error) => reject(error)
326 onZonegroupChange() {
327 const zoneGroupControl = this.storageClassForm.get('zonegroup').value;
328 const selectedZoneGroup = this.zoneGroupDetails.zonegroups.find(
329 (zonegroup) => zonegroup.name === zoneGroupControl
331 const defaultPlacementTarget = selectedZoneGroup.placement_targets.find(
332 (target: Target) => target.name === DEFAULT_PLACEMENT
334 if (selectedZoneGroup) {
335 const placementTargetNames = selectedZoneGroup.placement_targets.map(
336 (target: Target) => target.name
338 this.placementTargets = placementTargetNames;
340 if (defaultPlacementTarget && !this.editing) {
341 this.storageClassForm.get('placement_target').setValue(defaultPlacementTarget.name);
343 this.storageClassForm
344 .get('placement_target')
345 .setValue(this.storageClassInfo.placement_target);
350 const component = this;
351 const requestModel = this.buildRequest();
352 const storageclassName = this.storageClassForm.get('storage_class').value;
354 this.rgwStorageService.editStorageClass(requestModel).subscribe(
356 this.notificationService.show(
357 NotificationType.success,
358 $localize`Updated Storage Class '${storageclassName}'`
363 component.storageClassForm.setErrors({ cdSubmitButton: true });
367 this.rgwStorageService.createStorageClass(requestModel).subscribe(
369 this.notificationService.show(
370 NotificationType.success,
371 $localize`Created Storage Class '${storageclassName}'`
376 component.storageClassForm.setErrors({ cdSubmitButton: true });
383 this.router.navigate([`rgw/tiering`]);
386 getTierTargetByStorageClass(placementTargetInfo: PlacementTarget, storageClass: string) {
387 const tierTarget = placementTargetInfo.tier_targets.find(
388 (target: TierTarget) => target.val.storage_class === storageClass
393 onAllowReadThroughChange(checked: boolean): void {
394 this.allowReadThrough = checked;
395 const readThroughDaysControl = this.storageClassForm.get('read_through_restore_days');
396 if (this.allowReadThrough) {
397 this.storageClassForm.get('retain_head_object')?.setValue(true);
398 this.storageClassForm.get('retain_head_object')?.disable();
399 readThroughDaysControl?.enable();
401 this.storageClassForm.get('retain_head_object')?.enable();
402 readThroughDaysControl?.disable();
406 isTierMatch(...types: string[]): boolean {
407 const tierType = this.storageClassForm.getValue('storageClassType');
408 return types.includes(tierType);
412 if (this.storageClassForm.errors) return null;
414 const rawFormValue = _.cloneDeep(this.storageClassForm.value);
415 const zoneGroup = this.storageClassForm.get('zonegroup').value;
416 const storageClass = this.storageClassForm.get('storage_class').value;
417 const placementId = this.storageClassForm.get('placement_target').value;
418 const storageClassType = this.storageClassForm.get('storageClassType').value;
419 const retain_head_object = this.storageClassForm.get('retain_head_object').value;
420 return this.buildPlacementTargets(
430 private buildPlacementTargets(
431 storageClassType: string,
434 storageClass: string,
435 retain_head_object: boolean,
439 placement_id: placementId,
440 storage_class: storageClass
443 if (storageClassType === TIER_TYPE.LOCAL) {
445 zone_group: zoneGroup,
446 placement_targets: [baseTarget]
451 endpoint: rawFormValue.endpoint,
452 access_key: rawFormValue.access_key,
453 secret: rawFormValue.secret_key,
454 target_path: rawFormValue.target_path,
456 allow_read_through: rawFormValue.allow_read_through,
457 region: rawFormValue.region,
458 multipart_sync_threshold: rawFormValue.multipart_sync_threshold,
459 multipart_min_part_size: rawFormValue.multipart_min_part_size,
460 restore_storage_class: rawFormValue.restore_storage_class,
461 ...(rawFormValue.allow_read_through
462 ? { read_through_restore_days: rawFormValue.read_through_restore_days }
466 if (storageClassType === TIER_TYPE.CLOUD_TIER) {
468 zone_group: zoneGroup,
472 tier_type: TIER_TYPE.CLOUD_TIER,
481 if (storageClassType === TIER_TYPE.GLACIER) {
483 zone_group: zoneGroup,
487 tier_type: TIER_TYPE.GLACIER,
490 glacier_restore_days: rawFormValue.glacier_restore_days,
491 glacier_restore_tier_type: rawFormValue.glacier_restore_tier_type
498 zone_group: zoneGroup,
499 placement_targets: [baseTarget]