From: Abhishek Desai Date: Mon, 20 Apr 2026 05:44:28 +0000 (+0530) Subject: mgr/dashboard : fix-non-versioning-bucket-issue X-Git-Url: http://git-server-git.apps.pok.os.sepia.ceph.com/?a=commitdiff_plain;h=14d958ae2dab8442d2432fce0005bffa7dc75b85;p=ceph.git mgr/dashboard : fix-non-versioning-bucket-issue fixes : https://tracker.ceph.com/issues/76109 Signed-off-by: Abhishek Desai --- diff --git a/qa/tasks/mgr/dashboard/test_rgw.py b/qa/tasks/mgr/dashboard/test_rgw.py index 96936f55047f..3f385f79a27f 100644 --- a/qa/tasks/mgr/dashboard/test_rgw.py +++ b/qa/tasks/mgr/dashboard/test_rgw.py @@ -204,7 +204,7 @@ class RgwBucketTest(RgwTestCase): self.assertEqual(data['bucket'], 'teuth-test-bucket') self.assertEqual(data['owner'], 'admin') self.assertEqual(data['placement_rule'], 'default-placement') - self.assertEqual(data['versioning'], 'Suspended') + self.assertEqual(data['versioning'], 'Off') # Update bucket: change owner, enable versioning. self._put( @@ -312,7 +312,7 @@ class RgwBucketTest(RgwTestCase): # Get the bucket. data = _verify_tenant_bucket('teuth-test-bucket', 'testx', 'teuth-test-user') self.assertEqual(data['placement_rule'], 'default-placement') - self.assertEqual(data['versioning'], 'Suspended') + self.assertEqual(data['versioning'], 'Off') # Update bucket: different user with different tenant, enable versioning. self._put( diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/rgw/models/rgw-bucket-versioning.ts b/src/pybind/mgr/dashboard/frontend/src/app/ceph/rgw/models/rgw-bucket-versioning.ts index 51048c65e7b3..77160c0c5cba 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/ceph/rgw/models/rgw-bucket-versioning.ts +++ b/src/pybind/mgr/dashboard/frontend/src/app/ceph/rgw/models/rgw-bucket-versioning.ts @@ -1,4 +1,5 @@ export enum RgwBucketVersioning { ENABLED = 'Enabled', - SUSPENDED = 'Suspended' + SUSPENDED = 'Suspended', + OFF = 'Off' } diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/rgw/rgw-bucket-form/rgw-bucket-form.component.ts b/src/pybind/mgr/dashboard/frontend/src/app/ceph/rgw/rgw-bucket-form/rgw-bucket-form.component.ts index 512f0462c190..6a1ed6575721 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/ceph/rgw/rgw-bucket-form/rgw-bucket-form.component.ts +++ b/src/pybind/mgr/dashboard/frontend/src/app/ceph/rgw/rgw-bucket-form/rgw-bucket-form.component.ts @@ -68,6 +68,7 @@ export class RgwBucketFormComponent extends CdForm implements OnInit, AfterViewC placementTargets: object[] = []; isVersioningAlreadyEnabled = false; isMfaDeleteAlreadyEnabled = false; + initialVersioningStatus: string | null = null; icons = Icons; kmsConfigured = false; s3Configured = false; @@ -267,6 +268,9 @@ export class RgwBucketFormComponent extends CdForm implements OnInit, AfterViewC if (data['getBid']) { const bidResp = data['getBid']; + if (this.editing) { + this.initialVersioningStatus = bidResp['versioning'] ?? null; + } // Get the default values (incl. the values from disabled fields). const defaults = _.clone(this.bucketForm.getRawValue()); @@ -526,8 +530,14 @@ export class RgwBucketFormComponent extends CdForm implements OnInit, AfterViewC mfaTokenPinControl.updateValueAndValidity(); } - getVersioningStatus() { - return this.isVersioningEnabled ? RgwBucketVersioning.ENABLED : RgwBucketVersioning.SUSPENDED; + getVersioningStatus(): string { + if (this.isVersioningEnabled) { + return RgwBucketVersioning.ENABLED; + } + if (this.editing && this.initialVersioningStatus === RgwBucketVersioning.OFF) { + return ''; + } + return RgwBucketVersioning.SUSPENDED; } getMfaDeleteStatus() { diff --git a/src/pybind/mgr/dashboard/frontend/src/app/shared/api/rgw-bucket.service.spec.ts b/src/pybind/mgr/dashboard/frontend/src/app/shared/api/rgw-bucket.service.spec.ts index 6375cf3d4eac..d09ca9ae8f2e 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/shared/api/rgw-bucket.service.spec.ts +++ b/src/pybind/mgr/dashboard/frontend/src/app/shared/api/rgw-bucket.service.spec.ts @@ -95,7 +95,35 @@ describe('RgwBucketService', () => { ) .subscribe(); const req = httpTesting.expectOne( - `api/rgw/bucket/foo?${RgwHelper.DAEMON_QUERY_PARAM}&bucket_id=bar&uid=baz&versioning_state=Enabled&encryption_state=true&encryption_type=aws%253Akms&key_id=qwerty1&mfa_delete=Enabled&mfa_token_serial=1&mfa_token_pin=223344&lock_mode=GOVERNANCE&lock_retention_period_days=10&tags=null&bucket_policy=null&canned_acl=private&replication=true&lifecycle=null` + `api/rgw/bucket/foo?${RgwHelper.DAEMON_QUERY_PARAM}&bucket_id=bar&uid=baz&encryption_state=true&encryption_type=aws%253Akms&key_id=qwerty1&mfa_delete=Enabled&mfa_token_serial=1&mfa_token_pin=223344&lock_mode=GOVERNANCE&lock_retention_period_days=10&tags=null&bucket_policy=null&canned_acl=private&replication=true&lifecycle=null&versioning_state=Enabled` + ); + expect(req.request.method).toBe('PUT'); + }); + + it('should call update without versioning_state when empty', () => { + service + .update( + 'foo', + 'bar', + 'baz', + '', + true, + 'aws:kms', + 'qwerty1', + 'Enabled', + '1', + '223344', + 'GOVERNANCE', + '10', + null, + null, + 'private', + 'true', + null + ) + .subscribe(); + const req = httpTesting.expectOne( + `api/rgw/bucket/foo?${RgwHelper.DAEMON_QUERY_PARAM}&bucket_id=bar&uid=baz&encryption_state=true&encryption_type=aws%253Akms&key_id=qwerty1&mfa_delete=Enabled&mfa_token_serial=1&mfa_token_pin=223344&lock_mode=GOVERNANCE&lock_retention_period_days=10&tags=null&bucket_policy=null&canned_acl=private&replication=true&lifecycle=null` ); expect(req.request.method).toBe('PUT'); }); diff --git a/src/pybind/mgr/dashboard/frontend/src/app/shared/api/rgw-bucket.service.ts b/src/pybind/mgr/dashboard/frontend/src/app/shared/api/rgw-bucket.service.ts index 5f82e30fc081..cb7f7023d39f 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/shared/api/rgw-bucket.service.ts +++ b/src/pybind/mgr/dashboard/frontend/src/app/shared/api/rgw-bucket.service.ts @@ -170,10 +170,9 @@ export class RgwBucketService extends ApiClient { lifecycle: string ) { return this.rgwDaemonService.request((params: HttpParams) => { - params = params.appendAll({ + const paramsObject: Record = { bucket_id: bucketId, uid: uid, - versioning_state: versioningState, encryption_state: String(encryptionState), encryption_type: encryptionType, key_id: keyId, @@ -187,7 +186,11 @@ export class RgwBucketService extends ApiClient { canned_acl: cannedAcl, replication: replication, lifecycle: lifecycle - }); + }; + if (versioningState) { + paramsObject['versioning_state'] = versioningState; + } + params = params.appendAll(paramsObject); return this.http.put(`${this.url}/${bucket}`, null, { params: params }); }); } diff --git a/src/pybind/mgr/dashboard/services/rgw_client.py b/src/pybind/mgr/dashboard/services/rgw_client.py index 881cb0c63723..644df47f2132 100755 --- a/src/pybind/mgr/dashboard/services/rgw_client.py +++ b/src/pybind/mgr/dashboard/services/rgw_client.py @@ -599,8 +599,12 @@ class RgwClient(RestClient): """ # pylint: disable=unused-argument result = request() - if 'Status' not in result: - result['Status'] = 'Suspended' + if not isinstance(result, dict): + result = {} + # RGW omits Status when versioning has never been configured (CLI/radosgw-admin + # reports "off"). That must not be shown as "Suspended", which means versioning was enabled + if not result.get('Status'): + result['Status'] = 'Off' if 'MfaDelete' not in result: result['MfaDelete'] = 'Disabled' return result