From a4558ba6b186fb2c37673d032843855f332e57b6 Mon Sep 17 00:00:00 2001 From: Volker Theile Date: Wed, 22 May 2019 11:30:50 +0200 Subject: [PATCH] mgr/dashboard: Fix various RGW issues * Fix handling of tenanted users * Better exception handling if RGW backend is not available * RGW user/bucket "max. size" should be hidden when "user/bucket quota" is not enabled * Fix bug in loading user quota. Fixes: https://tracker.ceph.com/issues/38800 partial manual backport of 61995970916cb11f66c3800bcf869755f8dae32c partial manual backport of e6f130d47023c24f8a9c742f145abd34d7320cd8 partial manual backport of 945e790cab57785e1bc2dbf708d5ddafec618aba partial manual backport of a98bca6a2f161140f78f34cfa5f5ebec07af2f82 partial manual backport of 975736aba546adb09ce4f38a1ae061621f7240cd This fix is not cherry-picked because the code was completely re-worked for Nautilus (codebase is too far apart). Signed-off-by: Volker Theile --- .gitignore | 2 + qa/tasks/mgr/dashboard/helper.py | 3 + qa/tasks/mgr/dashboard/test_rgw.py | 351 +++++++++++++++++- src/pybind/mgr/dashboard/controllers/rgw.py | 89 ++++- .../frontend/src/app/app-routing.module.ts | 2 +- .../rgw-bucket-details.component.html | 2 +- .../rgw-bucket-form.component.html | 15 +- .../rgw-bucket-form.component.ts | 12 +- .../rgw-bucket-list.component.html | 4 +- .../rgw-bucket-list.component.ts | 2 +- .../rgw-user-details.component.html | 2 +- .../rgw-user-form.component.html | 20 +- .../rgw-user-form/rgw-user-form.component.ts | 39 +- .../rgw-user-list.component.html | 6 +- .../rgw-user-list/rgw-user-list.component.ts | 4 +- .../src/app/shared/api/rgw-bucket.service.ts | 15 +- .../src/app/shared/api/rgw-user.service.ts | 4 +- 17 files changed, 491 insertions(+), 81 deletions(-) diff --git a/.gitignore b/.gitignore index 7d42fb41f19..e498c555309 100644 --- a/.gitignore +++ b/.gitignore @@ -61,3 +61,5 @@ GTAGS # editor backup files etc. \#*\# + +.idea diff --git a/qa/tasks/mgr/dashboard/helper.py b/qa/tasks/mgr/dashboard/helper.py index 1be8243d34d..1f10ba8899b 100644 --- a/qa/tasks/mgr/dashboard/helper.py +++ b/qa/tasks/mgr/dashboard/helper.py @@ -95,6 +95,9 @@ class DashboardTestCase(MgrTestCase): else: assert False try: + if not cls._resp.ok: + # Output response for easier debugging. + log.error("Request response: %s", cls._resp.text) if cls._resp.text and cls._resp.text != "": return cls._resp.json() return cls._resp.text diff --git a/qa/tasks/mgr/dashboard/test_rgw.py b/qa/tasks/mgr/dashboard/test_rgw.py index 0c73b142a4c..70a0f6fac47 100644 --- a/qa/tasks/mgr/dashboard/test_rgw.py +++ b/qa/tasks/mgr/dashboard/test_rgw.py @@ -4,7 +4,7 @@ import urllib import logging logger = logging.getLogger(__name__) -from .helper import DashboardTestCase, authenticate +from .helper import DashboardTestCase, authenticate, JObj, JList, JLeaf class RgwControllerTest(DashboardTestCase): @@ -53,7 +53,6 @@ class RgwProxyExceptionsTest(DashboardTestCase): @classmethod def setUpClass(cls): super(RgwProxyExceptionsTest, cls).setUpClass() - cls._ceph_cmd(['dashboard', 'set-rgw-api-secret-key', '']) cls._ceph_cmd(['dashboard', 'set-rgw-api-access-key', '']) @@ -65,6 +64,7 @@ class RgwProxyExceptionsTest(DashboardTestCase): class RgwProxyTest(DashboardTestCase): + @classmethod def setUpClass(cls): super(RgwProxyTest, cls).setUpClass() @@ -101,14 +101,20 @@ class RgwProxyTest(DashboardTestCase): self.assertStatus(200) data = self._get( - '/api/rgw/proxy/user', params={'uid': 'teuth-test-user'}) + '/api/rgw/proxy/user', + params={ + 'uid': 'teuth-test-user' + }) self.assertStatus(200) self.assertEqual(data['user_id'], 'teuth-test-user') def _test_get(self): data = self._get( - '/api/rgw/proxy/user', params={'uid': 'teuth-test-user'}) + '/api/rgw/proxy/user', + params={ + 'uid': 'teuth-test-user' + }) self._assert_user_data(data) self.assertStatus(200) @@ -128,10 +134,9 @@ class RgwProxyTest(DashboardTestCase): self.assertEqual(self._resp.json()['display_name'], 'new name') def _test_delete(self): - self._delete('/api/rgw/proxy/user', params={'uid': 'teuth-test-user'}) - self.assertStatus(200) - - self._delete('/api/rgw/proxy/user', params={'uid': 'teuth-test-user'}) + self._delete('/api/rgw/user/teuth-test-user') + self.assertStatus(204) + self._delete('/api/rgw/user/teuth-test-user') self.assertStatus(500) resp = self._resp.json() self.assertIn('detail', resp) @@ -140,6 +145,13 @@ class RgwProxyTest(DashboardTestCase): self.assertIn('"HostId"', resp['detail']) self.assertIn('"RequestId"', resp['detail']) + @authenticate + def test_user_list(self): + data = self._get('/api/rgw/proxy/metadata/user') + self.assertStatus(200) + self.assertGreaterEqual(len(data), 1) + self.assertIn('admin', data) + @authenticate def test_rgw_proxy(self): """Test basic request types""" @@ -156,3 +168,326 @@ class RgwProxyTest(DashboardTestCase): # DELETE - Delete the user self._test_delete() + + +class RgwTestCase(DashboardTestCase): + + maxDiff = None + create_test_user = False + + @classmethod + def setUpClass(cls): + super(RgwTestCase, cls).setUpClass() + # Create the administrator account. + cls._radosgw_admin_cmd([ + 'user', 'create', '--uid=admin', '--display-name=admin', + '--system', '--access-key=admin', '--secret=admin' + ]) + # Update the dashboard configuration. + cls._ceph_cmd(['dashboard', 'set-rgw-api-secret-key', 'admin']) + cls._ceph_cmd(['dashboard', 'set-rgw-api-access-key', 'admin']) + # Create a test user? + if cls.create_test_user: + cls._radosgw_admin_cmd([ + 'user', 'create', '--uid', 'teuth-test-user', '--display-name', + 'teuth-test-user' + ]) + cls._radosgw_admin_cmd([ + 'caps', 'add', '--uid', 'teuth-test-user', '--caps', + 'metadata=write' + ]) + cls._radosgw_admin_cmd([ + 'subuser', 'create', '--uid', 'teuth-test-user', '--subuser', + 'teuth-test-subuser', '--access', 'full', '--key-type', 's3', + '--access-key', 'xyz123' + ]) + cls._radosgw_admin_cmd([ + 'subuser', 'create', '--uid', 'teuth-test-user', '--subuser', + 'teuth-test-subuser2', '--access', 'full', '--key-type', + 'swift' + ]) + + @classmethod + def tearDownClass(cls): + if cls.create_test_user: + cls._radosgw_admin_cmd(['user', 'rm', '--uid=teuth-test-user']) + super(RgwTestCase, cls).tearDownClass() + + +class RgwBucketTest(RgwTestCase): + + @classmethod + def setUpClass(cls): + cls.create_test_user = True + super(RgwBucketTest, cls).setUpClass() + # Create a tenanted user. + cls._radosgw_admin_cmd([ + 'user', 'create', '--tenant', 'testx', '--uid', 'teuth-test-user', + '--display-name', 'tenanted teuth-test-user' + ]) + + @classmethod + def tearDownClass(cls): + cls._radosgw_admin_cmd( + ['user', 'rm', '--tenant', 'testx', '--uid=teuth-test-user']) + super(RgwBucketTest, cls).tearDownClass() + + @authenticate + def test_all(self): + # Create a new bucket. + self._post( + '/api/rgw/bucket', + params={ + 'bucket': 'teuth-test-bucket', + 'uid': 'admin' + }) + self.assertStatus(201) + data = self.jsonBody() + self.assertSchema(data, JObj(sub_elems={ + 'bucket_info': JObj(sub_elems={ + 'bucket': JObj(allow_unknown=True, sub_elems={ + 'name': JLeaf(str), + 'bucket_id': JLeaf(str), + 'tenant': JLeaf(str) + }), + 'quota': JObj(sub_elems={}, allow_unknown=True), + 'creation_time': JLeaf(str) + }, allow_unknown=True) + }, allow_unknown=True)) + data = data['bucket_info']['bucket'] + self.assertEqual(data['name'], 'teuth-test-bucket') + self.assertEqual(data['tenant'], '') + + # List all buckets. + data = self._get('/api/rgw/proxy/bucket') + self.assertStatus(200) + self.assertEqual(len(data), 1) + self.assertIn('teuth-test-bucket', data) + + # Get the bucket. + data = self._get('/api/rgw/bucket/teuth-test-bucket') + self.assertStatus(200) + self.assertSchema(data, JObj(sub_elems={ + 'id': JLeaf(str), + 'bid': JLeaf(str), + 'tenant': JLeaf(str), + 'bucket': JLeaf(str), + 'bucket_quota': JObj(sub_elems={}, allow_unknown=True), + 'owner': JLeaf(str) + }, allow_unknown=True)) + self.assertEqual(data['bucket'], 'teuth-test-bucket') + self.assertEqual(data['owner'], 'admin') + + # Update the bucket. + self._put( + '/api/rgw/proxy/bucket', + params={ + 'bucket': 'teuth-test-bucket', + 'bucket-id': data['id'], + 'uid': 'teuth-test-user' + }) + self.assertStatus(200) + data = self._get('/api/rgw/bucket/teuth-test-bucket') + self.assertStatus(200) + self.assertSchema(data, JObj(sub_elems={ + 'owner': JLeaf(str), + 'bid': JLeaf(str), + 'tenant': JLeaf(str) + }, allow_unknown=True)) + self.assertEqual(data['owner'], 'teuth-test-user') + + # Delete the bucket. + self._delete('/api/rgw/proxy/bucket', params={ + 'bucket': 'teuth-test-bucket', + 'purge-objects': 'true' + }) + self.assertStatus(200) + data = self._get('/api/rgw/proxy/bucket') + self.assertStatus(200) + self.assertEqual(len(data), 0) + + @authenticate + def test_create_get_update_delete_w_tenant(self): + # Create a new bucket. The tenant of the user is used when + # the bucket is created. + self._post( + '/api/rgw/bucket', + params={ + 'bucket': 'teuth-test-bucket', + 'uid': 'testx$teuth-test-user' + }) + self.assertStatus(201) + # It's not possible to validate the result because there + # IS NO result object returned by the RGW Admin OPS API + # when a tenanted bucket is created. + data = self.jsonBody() + self.assertIsNone(data) + + # List all buckets. + data = self._get('/api/rgw/proxy/bucket') + self.assertStatus(200) + self.assertEqual(len(data), 1) + self.assertIn('testx/teuth-test-bucket', data) + + # Get the bucket. + data = self._get('/api/rgw/bucket/{}'.format( + urllib.quote_plus('testx/teuth-test-bucket'))) + self.assertStatus(200) + self.assertSchema(data, JObj(sub_elems={ + 'owner': JLeaf(str), + 'bucket': JLeaf(str), + 'tenant': JLeaf(str), + 'bid': JLeaf(str) + }, allow_unknown=True)) + self.assertEqual(data['owner'], 'testx$teuth-test-user') + self.assertEqual(data['bucket'], 'teuth-test-bucket') + self.assertEqual(data['tenant'], 'testx') + self.assertEqual(data['bid'], 'testx/teuth-test-bucket') + + # Update the bucket. + # Currently it is not possible to link such a bucket that is + # owned by a tenanted user to a 'normal' user. + # See http://tracker.ceph.com/issues/39991 + #self._put( + # '/api/rgw/proxy/bucket', + # params={ + # 'bucket': urllib.quote_plus('testx/teuth-test-bucket'), + # 'bucket-id': data['id'], + # 'uid': 'admin' + # }) + #self.assertStatus(200) + #data = self._get('/api/rgw/bucket/{}'.format( + # urllib.quote_plus('testx/teuth-test-bucket'))) + #self.assertStatus(200) + #self.assertIn('owner', data) + #self.assertEqual(data['owner'], 'admin') + + # Delete the bucket. + self._delete('/api/rgw/proxy/bucket', params={ + 'bucket': urllib.quote_plus('testx/teuth-test-bucket'), + 'purge-objects': 'true' + }) + self.assertStatus(200) + data = self._get('/api/rgw/proxy/bucket') + self.assertStatus(200) + self.assertEqual(len(data), 0) + + +class RgwUserTest(RgwTestCase): + + def _assert_user_data(self, data): + self.assertSchema(data, JObj(sub_elems={ + 'caps': JList(JObj(sub_elems={}, allow_unknown=True)), + 'display_name': JLeaf(str), + 'email': JLeaf(str), + 'keys': JList(JObj(sub_elems={}, allow_unknown=True)), + 'max_buckets': JLeaf(int), + 'subusers': JList(JLeaf(str)), + 'suspended': JLeaf(int), + 'swift_keys': JList(JObj(sub_elems={}, allow_unknown=True)), + 'tenant': JLeaf(str), + 'user_id': JLeaf(str) + }, allow_unknown=True)) + self.assertGreaterEqual(len(data['keys']), 1) + + @authenticate + def test_get(self): + data = self._get('/api/rgw/user/admin') + self.assertStatus(200) + self._assert_user_data(data) + self.assertIn('uid', data) + self.assertEqual(data['user_id'], 'admin') + + @authenticate + def test_create_get_update_delete(self): + # Create a new user. + self._put( + '/api/rgw/proxy/user', + params={ + 'uid': 'teuth-test-user', + 'display-name': 'display name' + }) + self.assertStatus(200) + data = self.jsonBody() + self._assert_user_data(data) + self.assertEqual(data['user_id'], 'teuth-test-user') + self.assertEqual(data['display_name'], 'display name') + + # Get the user. + data = self._get('/api/rgw/user/teuth-test-user') + self.assertStatus(200) + self._assert_user_data(data) + self.assertEqual(data['tenant'], '') + self.assertEqual(data['user_id'], 'teuth-test-user') + self.assertEqual(data['uid'], 'teuth-test-user') + + # Update the user. + self._post( + '/api/rgw/proxy/user', + params={ + 'uid': 'teuth-test-user', + 'display-name': 'new name', + }) + self.assertStatus(200) + data = self.jsonBody() + self._assert_user_data(data) + self.assertEqual(data['display_name'], 'new name') + + # Delete the user. + self._delete('/api/rgw/user/teuth-test-user') + self.assertStatus(204) + self._get('/api/rgw/user/teuth-test-user') + self.assertStatus(500) + resp = self.jsonBody() + self.assertIn('detail', resp) + self.assertIn('failed request with status code 404', resp['detail']) + self.assertIn('"Code":"NoSuchUser"', resp['detail']) + self.assertIn('"HostId"', resp['detail']) + self.assertIn('"RequestId"', resp['detail']) + + @authenticate + def test_create_get_update_delete_w_tenant(self): + # Create a new user. + self._put( + '/api/rgw/proxy/user', + params={ + 'uid': 'test01$teuth-test-user', + 'display-name': 'display name' + }) + self.assertStatus(200) + data = self.jsonBody() + self._assert_user_data(data) + self.assertEqual(data['user_id'], 'teuth-test-user') + self.assertEqual(data['display_name'], 'display name') + + # Get the user. + data = self._get('/api/rgw/user/test01$teuth-test-user') + self.assertStatus(200) + self._assert_user_data(data) + self.assertEqual(data['tenant'], 'test01') + self.assertEqual(data['user_id'], 'teuth-test-user') + self.assertEqual(data['uid'], 'test01$teuth-test-user') + + # Update the user. + self._post( + '/api/rgw/proxy/user', + params={ + 'uid': 'test01$teuth-test-user', + 'display-name': 'new name' + }) + self.assertStatus(200) + data = self.jsonBody() + self._assert_user_data(data) + self.assertEqual(data['display_name'], 'new name') + + # Delete the user. + self._delete('/api/rgw/user/test01$teuth-test-user') + self.assertStatus(204) + self._get('/api/rgw/user/test01$teuth-test-user') + self.assertStatus(500) + resp = self.jsonBody() + self.assertIn('detail', resp) + self.assertIn('failed request with status code 404', resp['detail']) + self.assertIn('"Code":"NoSuchUser"', resp['detail']) + self.assertIn('"HostId"', resp['detail']) + self.assertIn('"RequestId"', resp['detail']) diff --git a/src/pybind/mgr/dashboard/controllers/rgw.py b/src/pybind/mgr/dashboard/controllers/rgw.py index 0bc0daf0236..9951d24f58b 100644 --- a/src/pybind/mgr/dashboard/controllers/rgw.py +++ b/src/pybind/mgr/dashboard/controllers/rgw.py @@ -2,6 +2,7 @@ from __future__ import absolute_import import json +import sys import cherrypy from . import ApiController, BaseController, RESTController, AuthRequired @@ -10,6 +11,11 @@ from ..services.ceph_service import CephService from ..services.rgw_client import RgwClient from ..rest_client import RequestException +if sys.version_info >= (3, 0): + from urllib.parse import unquote # pylint: disable=no-name-in-module,import-error +else: + from urllib import unquote # pylint: disable=no-name-in-module + @ApiController('rgw') @AuthRequired() @@ -32,8 +38,8 @@ class Rgw(RESTController): instance.userid) raise RequestException(status['message']) status['available'] = True - except RequestException: - pass + except (RequestException, LookupError) as ex: + status['message'] = str(ex) return status @@ -99,6 +105,12 @@ class RgwProxy(BaseController): if cherrypy.request.body.length: data = cherrypy.request.body.read() + for key, value in params.items(): + # pylint: disable=undefined-variable + if (sys.version_info < (3, 0) and isinstance(value, unicode)) \ + or isinstance(value, str): + params[key] = unquote(value) + return rgw_client.proxy(method, path, params, data) except RequestException as e: # Always use status code 500 and NOT the status that may delivered @@ -109,9 +121,49 @@ class RgwProxy(BaseController): return json.dumps({'detail': str(e)}).encode('utf-8') +class RgwRESTController(RESTController): + + @staticmethod + def proxy(method, path, params=None, json_response=True): + instance = RgwClient.admin_instance() + result = instance.proxy(method, path, params, None) + if json_response: + result = result.decode('utf-8') + if result != '': + result = json.loads(result) + else: + result = {} + return result + + @ApiController('rgw/bucket') @AuthRequired() -class RgwBucket(RESTController): +class RgwBucket(RgwRESTController): + + @staticmethod + def _append_bid(bucket): + """ + Append the bucket identifier that looks like [/]. + See http://docs.ceph.com/docs/nautilus/radosgw/multitenancy/ for + more information. + :param bucket: The bucket parameters. + :type bucket: dict + :return: The modified bucket parameters including the 'bid' parameter. + :rtype: dict + """ + if isinstance(bucket, dict): + bucket['bid'] = '{}/{}'.format(bucket['tenant'], bucket['bucket']) \ + if bucket['tenant'] else bucket['bucket'] + return bucket + + def get(self, bucket): + try: + result = self.proxy('GET', 'bucket', {'bucket': bucket}) + return self._append_bid(result) + except RequestException as e: + cherrypy.response.headers['Content-Type'] = 'application/json' + cherrypy.response.status = 500 + return {'detail': str(e)} def create(self, bucket, uid): try: @@ -125,20 +177,43 @@ class RgwBucket(RESTController): @ApiController('rgw/user') @AuthRequired() -class RgwUser(RESTController): +class RgwUser(RgwRESTController): + + @staticmethod + def _append_uid(user): + """ + Append the user identifier that looks like [$]. + See http://docs.ceph.com/docs/jewel/radosgw/multitenancy/ for + more information. + :param user: The user parameters. + :type user: dict + :return: The modified user parameters including the 'uid' parameter. + :rtype: dict + """ + if isinstance(user, dict): + user['uid'] = '{}${}'.format(user['tenant'], user['user_id']) \ + if user['tenant'] else user['user_id'] + return user + + def get(self, uid): + try: + result = self.proxy('GET', 'user', {'uid': uid}) + return self._append_uid(result) + except RequestException as e: + cherrypy.response.headers['Content-Type'] = 'application/json' + cherrypy.response.status = 500 + return {'detail': str(e)} def delete(self, uid): try: rgw_client = RgwClient.admin_instance() - # Ensure the user is not configured to access the Object Gateway. if rgw_client.userid == uid: raise RequestException('Unable to delete "{}" - this user ' 'account is required for managing the ' 'Object Gateway'.format(uid)) - # Finally redirect request to the RGW proxy. - return rgw_client.proxy('DELETE', 'user', cherrypy.request.params, None) + return self.proxy('DELETE', 'user', cherrypy.request.params) except RequestException as e: cherrypy.response.headers['Content-Type'] = 'application/json' cherrypy.response.status = 500 diff --git a/src/pybind/mgr/dashboard/frontend/src/app/app-routing.module.ts b/src/pybind/mgr/dashboard/frontend/src/app/app-routing.module.ts index bca500fff68..1a324a51e07 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/app-routing.module.ts +++ b/src/pybind/mgr/dashboard/frontend/src/app/app-routing.module.ts @@ -75,7 +75,7 @@ const routes: Routes = [ canActivate: [AuthGuardService] }, { - path: 'bucket/edit/:bucket', + path: 'bucket/edit/:bid', component: RgwBucketFormComponent, canActivate: [AuthGuardService] } diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/rgw/rgw-bucket-details/rgw-bucket-details.component.html b/src/pybind/mgr/dashboard/frontend/src/app/ceph/rgw/rgw-bucket-details/rgw-bucket-details.component.html index 306d6de9d02..3ccb9cb684f 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/ceph/rgw/rgw-bucket-details/rgw-bucket-details.component.html +++ b/src/pybind/mgr/dashboard/frontend/src/app/ceph/rgw/rgw-bucket-details/rgw-bucket-details.component.html @@ -6,7 +6,7 @@ Name - {{ bucket.bucket }} + {{ bucket.bid }}
+ [ngClass]="{'has-error': (frm.submitted || bucketForm.controls.bid.dirty) && bucketForm.controls.bid.invalid}">
- + *ngIf="(frm.submitted || bucketForm.controls.bid.dirty) && bucketForm.controls.bid.hasError('required')"> This field is required. + *ngIf="(frm.submitted || bucketForm.controls.bid.dirty) && bucketForm.controls.bid.hasError('bucketNameInvalid')"> The value is not valid. + *ngIf="(frm.submitted || bucketForm.controls.bid.dirty) && bucketForm.controls.bid.hasError('bucketNameExists')"> The chosen name is already in use.
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 6d70a176c63..4ec17395b55 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 @@ -39,7 +39,7 @@ export class RgwBucketFormComponent implements OnInit { createForm() { this.bucketForm = this.formBuilder.group({ id: [null], - bucket: [null, [Validators.required], [this.bucketNameValidator()]], + bid: [null, [Validators.required], [this.bucketNameValidator()]], owner: [null, [Validators.required]] }); } @@ -52,15 +52,15 @@ export class RgwBucketFormComponent implements OnInit { // Process route parameters. this.route.params.subscribe( - (params: { bucket: string }) => { - if (!params.hasOwnProperty('bucket')) { + (params: { bid: string }) => { + if (!params.hasOwnProperty('bid')) { return; } - params.bucket = decodeURIComponent(params.bucket); + const bid = decodeURIComponent(params.bid); this.loading = true; // Load the bucket data in 'edit' mode. this.editing = true; - this.rgwBucketService.get(params.bucket).subscribe((resp: object) => { + this.rgwBucketService.get(bid).subscribe((resp: object) => { this.loading = false; // Get the default values. const defaults = _.clone(this.bucketForm.value); @@ -87,7 +87,7 @@ export class RgwBucketFormComponent implements OnInit { if (this.bucketForm.pristine) { this.goToListView(); } - const bucketCtl = this.bucketForm.get('bucket'); + const bucketCtl = this.bucketForm.get('bid'); const ownerCtl = this.bucketForm.get('owner'); if (this.editing) { // Edit diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/rgw/rgw-bucket-list/rgw-bucket-list.component.html b/src/pybind/mgr/dashboard/frontend/src/app/ceph/rgw/rgw-bucket-list/rgw-bucket-list.component.html index 4355083213a..59215b6c1b7 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/ceph/rgw/rgw-bucket-list/rgw-bucket-list.component.html +++ b/src/pybind/mgr/dashboard/frontend/src/app/ceph/rgw/rgw-bucket-list/rgw-bucket-list.component.html @@ -29,7 +29,7 @@ @@ -60,7 +60,7 @@
  • Edit diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/rgw/rgw-bucket-list/rgw-bucket-list.component.ts b/src/pybind/mgr/dashboard/frontend/src/app/ceph/rgw/rgw-bucket-list/rgw-bucket-list.component.ts index 749460968e6..944499f9398 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/ceph/rgw/rgw-bucket-list/rgw-bucket-list.component.ts +++ b/src/pybind/mgr/dashboard/frontend/src/app/ceph/rgw/rgw-bucket-list/rgw-bucket-list.component.ts @@ -34,7 +34,7 @@ export class RgwBucketListComponent { this.columns = [ { name: 'Name', - prop: 'bucket', + prop: 'bid', flexGrow: 1 }, { diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/rgw/rgw-user-details/rgw-user-details.component.html b/src/pybind/mgr/dashboard/frontend/src/app/ceph/rgw/rgw-user-details/rgw-user-details.component.html index ca51f56970b..dae4103a2cd 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/ceph/rgw/rgw-user-details/rgw-user-details.component.html +++ b/src/pybind/mgr/dashboard/frontend/src/app/ceph/rgw/rgw-user-details/rgw-user-details.component.html @@ -6,7 +6,7 @@ Username - {{ user.user_id }} + {{ user.uid }}
    + [ngClass]="{'has-error': (frm.submitted || userForm.controls.uid.dirty) && userForm.controls.uid.invalid}">
    - This field is required. The chosen user ID is already in use. @@ -476,7 +476,7 @@
    + *ngIf="userForm.controls.user_quota_enabled.value && !userForm.controls.user_quota_max_size_unlimited.value">
  • Edit diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/rgw/rgw-user-list/rgw-user-list.component.ts b/src/pybind/mgr/dashboard/frontend/src/app/ceph/rgw/rgw-user-list/rgw-user-list.component.ts index 0e6b129f323..ae2f2f2936f 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/ceph/rgw/rgw-user-list/rgw-user-list.component.ts +++ b/src/pybind/mgr/dashboard/frontend/src/app/ceph/rgw/rgw-user-list/rgw-user-list.component.ts @@ -35,7 +35,7 @@ export class RgwUserListComponent { this.columns = [ { name: 'Username', - prop: 'user_id', + prop: 'uid', flexGrow: 1 }, { @@ -88,7 +88,7 @@ export class RgwUserListComponent { // Delete all selected data table rows. Observable.forkJoin( this.selection.selected.map((user: any) => { - return this.rgwUserService.delete(user.user_id); + return this.rgwUserService.delete(user.uid); }) ).subscribe( null, 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 2db300583cf..148049ca805 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 @@ -42,17 +42,14 @@ export class RgwBucketService { } get(bucket: string) { - let params = new HttpParams(); - params = params.append('bucket', bucket); - return this.http.get(this.url, {params: params}); + return this.http.get(`api/rgw/bucket/${bucket}`); } create(bucket: string, uid: string) { - const body = { - 'bucket': bucket, - 'uid': uid - }; - return this.http.post('api/rgw/bucket', body); + let params = new HttpParams(); + params = params.append('bucket', bucket); + params = params.append('uid', uid); + return this.http.post('api/rgw/bucket', null, {params: params}); } update(bucketId: string, bucket: string, uid: string) { @@ -60,7 +57,7 @@ export class RgwBucketService { params = params.append('bucket', bucket); params = params.append('bucket-id', bucketId as string); params = params.append('uid', uid); - return this.http.put(this.url, null, { params: params }); + return this.http.put(this.url, null, {params: params}); } delete(bucket: string, purgeObjects = true) { diff --git a/src/pybind/mgr/dashboard/frontend/src/app/shared/api/rgw-user.service.ts b/src/pybind/mgr/dashboard/frontend/src/app/shared/api/rgw-user.service.ts index f518f53e84d..9fcd87fab06 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/shared/api/rgw-user.service.ts +++ b/src/pybind/mgr/dashboard/frontend/src/app/shared/api/rgw-user.service.ts @@ -42,9 +42,7 @@ export class RgwUserService { } get(uid: string) { - let params = new HttpParams(); - params = params.append('uid', uid); - return this.http.get(this.url, {params: params}); + return this.http.get(`api/rgw/user/${uid}`); } getQuota(uid: string) { -- 2.47.3