From: Abhishek Desai Date: Tue, 17 Mar 2026 10:18:46 +0000 (+0530) Subject: mgr/dashboard : Restrict create storage class with existing name X-Git-Url: http://git-server-git.apps.pok.os.sepia.ceph.com/?a=commitdiff_plain;h=f144cf27ffb82c8386a606eed0019298364ad8be;p=ceph.git mgr/dashboard : Restrict create storage class with existing name fixes : Signed-off-by: Abhishek Desai --- diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/rgw/rgw-storage-class-form/rgw-storage-class-form.component.html b/src/pybind/mgr/dashboard/frontend/src/app/ceph/rgw/rgw-storage-class-form/rgw-storage-class-form.component.html index 1c0de9c9889d..ddd7ac68bcca 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/ceph/rgw/rgw-storage-class-form/rgw-storage-class-form.component.html +++ b/src/pybind/mgr/dashboard/frontend/src/app/ceph/rgw/rgw-storage-class-form/rgw-storage-class-form.component.html @@ -101,7 +101,7 @@ @if( isTierMatch(TIER_TYPE.LOCAL )){ -
+
Name + [invalid]="storageClass.isInvalid"/> + @if (storageClassForm.showError('storage_class', formDir, 'required')) { This field is required. + } @else if (storageClassForm.showError('storage_class', formDir, 'uniqueName')) { + The Storage Class name is already in use. + } diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/rgw/rgw-storage-class-form/rgw-storage-class-form.component.spec.ts b/src/pybind/mgr/dashboard/frontend/src/app/ceph/rgw/rgw-storage-class-form/rgw-storage-class-form.component.spec.ts index 1e0f852672db..3d50c6ea3cd6 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/ceph/rgw/rgw-storage-class-form/rgw-storage-class-form.component.spec.ts +++ b/src/pybind/mgr/dashboard/frontend/src/app/ceph/rgw/rgw-storage-class-form/rgw-storage-class-form.component.spec.ts @@ -126,6 +126,22 @@ describe('RgwStorageClassFormComponent', () => { expect(component).toBeTruthy(); }); + it('should mark duplicate storage class names as invalid', () => { + component.existingStorageClasses = [ + { + storage_class: 'storageClass1', + placement_target: 'placement1', + zonegroup_name: 'zonegroup1' + } + ]; + + const control = component.storageClassForm.get('storage_class'); + control.setValue('storageClass1'); + control.updateValueAndValidity(); + + expect(control.hasError('uniqueName')).toBe(true); + }); + it('should set required validators for CLOUD_TIER fields', () => { (component as any).updateValidatorsBasedOnStorageClass(TIER_TYPE_DISPLAY.CLOUD_TIER); const requiredFields = ['region', 'target_endpoint', 'access_key', 'secret_key', 'target_path']; diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/rgw/rgw-storage-class-form/rgw-storage-class-form.component.ts b/src/pybind/mgr/dashboard/frontend/src/app/ceph/rgw/rgw-storage-class-form/rgw-storage-class-form.component.ts index 59f04068eb4e..acb5eebd71dd 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/ceph/rgw/rgw-storage-class-form/rgw-storage-class-form.component.ts +++ b/src/pybind/mgr/dashboard/frontend/src/app/ceph/rgw/rgw-storage-class-form/rgw-storage-class-form.component.ts @@ -100,6 +100,7 @@ export class RgwStorageClassFormComponent extends CdForm implements OnInit { defaultZonegroup: ZoneGroup; zoneGroupDetails: ZoneGroupDetails; storageClassInfo: StorageClass; + existingStorageClasses: StorageClass[] = []; tierTargetInfo: TierTarget; glacierStorageClassDetails: S3Glacier; allowReadThrough: boolean = false; @@ -438,7 +439,14 @@ export class RgwStorageClassFormComponent extends CdForm implements OnInit { }); this.storageClassForm = this.formBuilder.group({ storage_class: new FormControl('', { - validators: [Validators.required] + validators: [ + Validators.required, + CdValidators.custom('uniqueName', (storageClassName: string) => { + return this.existingStorageClasses.some( + (storageClass: StorageClass) => storageClass.storage_class === storageClassName + ); + }) + ] }), zonegroup: new FormControl(this.selectedZoneGroup, { validators: [Validators.required] @@ -513,6 +521,8 @@ export class RgwStorageClassFormComponent extends CdForm implements OnInit { this.rgwZoneGroupService.getAllZonegroupsInfo().subscribe( (data: ZoneGroupDetails) => { this.zoneGroupDetails = data; + this.existingStorageClasses = BucketTieringUtils.filterAndMapTierTargets(data); + this.storageClassForm.get('storage_class')?.updateValueAndValidity({ emitEvent: false }); this.zonegroupNames = []; this.zones = []; if (data.zonegroups && data.zonegroups.length > 0) {