]> git.apps.os.sepia.ceph.com Git - ceph.git/commitdiff
mgr/dashboard: connect-rgw: rename to set-rgw-credentials; refactoring
authorAlfonso Martínez <almartin@redhat.com>
Fri, 6 Aug 2021 06:57:47 +0000 (08:57 +0200)
committerSebastian Wagner <sewagner@redhat.com>
Thu, 2 Sep 2021 14:24:37 +0000 (16:24 +0200)
- Rename the dashboard command to better reflect its behavior.
- Rename '_radosgw_admin' method to 'send_rgwadmin_command' for consistency with
  'send_mon_command' and move it to the mgr_module.py .
- Cleanup: remove unneeded rgw settings.
- Better error handling and test coverage.

Fixes: https://tracker.ceph.com/issues/44605
Signed-off-by: Alfonso Martínez <almartin@redhat.com>
(cherry picked from commit 6e20ef1dd35f3681d14cd4e08ca63eb20edc2c88)

doc/mgr/dashboard.rst
qa/tasks/mgr/dashboard/test_rgw.py
qa/tasks/mgr/dashboard/test_settings.py
src/pybind/mgr/cephadm/serve.py
src/pybind/mgr/dashboard/frontend/cypress/integration/cluster/mgr-modules.e2e-spec.ts
src/pybind/mgr/dashboard/module.py
src/pybind/mgr/dashboard/services/rgw_client.py
src/pybind/mgr/dashboard/settings.py
src/pybind/mgr/dashboard/tests/__init__.py
src/pybind/mgr/dashboard/tests/test_rgw_client.py
src/pybind/mgr/mgr_module.py

index 54a139a511468d9babb4eca0d07f2bbaa94d53d3..3ac0e0333b0a98661a142ebaccbfb31b9ac64fe1 100644 (file)
@@ -377,14 +377,18 @@ Enabling the Object Gateway Management Frontend
 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 When RGW is deployed with cephadm, the RGW credentials used by the
-dashboard will be automatically created.  You can also manually force the
-credentials to be created with::
+dashboard will be automatically configured. You can also manually force the
+credentials to be set up with::
 
-  $ ceph dashboard connect-rgw
+  $ ceph dashboard set-rgw-credentials
 
 This will create an RGW user with uid ``dashboard`` for each realm in
 the system.
 
+If you've configured a custom 'admin' resource in your RGW admin API, you should set it here also::
+
+  $ ceph dashboard set-rgw-api-admin-resource <admin_resource>
+
 If you are using a self-signed certificate in your Object Gateway setup,
 you should disable certificate verification in the dashboard to avoid refused
 connections, e.g. caused by certificates signed by unknown CA or not matching
index 525948a855c6534b3f0c6e5b6ee16cece9f6b0bc..b1a53a72140f0e23a1cdfbb95d5f4acb4518a6ff 100644 (file)
@@ -77,22 +77,22 @@ class RgwApiCredentialsTest(RgwTestCase):
         self.logout()
         self._ceph_cmd(['mgr', 'module', 'disable', 'dashboard'])
         self._ceph_cmd(['mgr', 'module', 'enable', 'dashboard', '--force'])
-        # Set the default credentials.
-        self._ceph_cmd_with_secret(['dashboard', 'set-rgw-api-secret-key'], 'admin')
-        self._ceph_cmd_with_secret(['dashboard', 'set-rgw-api-access-key'], 'admin')
         super(RgwApiCredentialsTest, self).setUp()
 
-    def test_no_access_secret_key(self):
-        self._ceph_cmd(['dashboard', 'reset-rgw-api-secret-key'])
-        self._ceph_cmd(['dashboard', 'reset-rgw-api-access-key'])
+    def test_invalid_credentials(self):
+        self._ceph_cmd_with_secret(['dashboard', 'set-rgw-api-secret-key'], 'invalid')
+        self._ceph_cmd_with_secret(['dashboard', 'set-rgw-api-access-key'], 'invalid')
         resp = self._get('/api/rgw/user')
         self.assertStatus(500)
         self.assertIn('detail', resp)
         self.assertIn('component', resp)
-        self.assertIn('No RGW credentials found', resp['detail'])
+        self.assertIn('Error connecting to Object Gateway', resp['detail'])
         self.assertEqual(resp['component'], 'rgw')
 
     def test_success(self):
+        # Set the default credentials.
+        self._ceph_cmd_with_secret(['dashboard', 'set-rgw-api-secret-key'], 'admin')
+        self._ceph_cmd_with_secret(['dashboard', 'set-rgw-api-access-key'], 'admin')
         data = self._get('/api/rgw/status')
         self.assertStatus(200)
         self.assertIn('available', data)
index 46750292e1c9447d002a9989f9731b42ff924d23..869ea2ca445fb8b4de88e66791ee536a1739b424 100644 (file)
@@ -51,8 +51,8 @@ class SettingsTest(DashboardTestCase):
 
     def test_bulk_set(self):
         self._put('/api/settings', {
-            'RGW_API_HOST': 'somehost',
-            'RGW_API_PORT': 7777,
+            'USER_PWD_EXPIRATION_WARNING_1': 12,
+            'USER_PWD_EXPIRATION_WARNING_2': 6,
         })
         self.assertStatus(200)
 
index 1c9bd5f4c56e5f3ca0394c267d9201e9c96a6287..071dd4cbef3c29f3a085fc6408841aa3f32018b6 100644 (file)
@@ -85,7 +85,7 @@ class CephadmServe:
                     self.mgr.need_connect_dashboard_rgw = False
                     if 'dashboard' in self.mgr.get('mgr_map')['modules']:
                         self.log.info('Checking dashboard <-> RGW credentials')
-                        self.mgr.remote('dashboard', 'connect_rgw')
+                        self.mgr.remote('dashboard', 'set_rgw_credentials')
 
                 if not self.mgr.paused:
                     self.mgr.to_remove_osds.process_removal_queue()
index 82c6878969935e787186961d320a55c4545a6536..50656feceb76b0f9ffe5567fc8a374b946c4da49 100644 (file)
@@ -29,11 +29,6 @@ describe('Manager modules page', () => {
 
     it('should test editing on dashboard module', () => {
       const dashboardArr: Input[] = [
-        {
-          id: 'RGW_API_USER_ID',
-          newValue: 'rq',
-          oldValue: ''
-        },
         {
           id: 'GRAFANA_API_PASSWORD',
           newValue: 'rafa',
index 287aaf48502fd4e9e5c12ab88c742293ee5b19d7..6abacebcfb35246dda32b350012eeae7579a0547 100644 (file)
@@ -401,12 +401,11 @@ class Module(MgrModule, CherryPyConfig):
             return result
         return 0, 'Self-signed certificate created', ''
 
-    @CLIWriteCommand("dashboard connect-rgw")
-    def connect_rgw(self):
+    @CLIWriteCommand("dashboard set-rgw-credentials")
+    def set_rgw_credentials(self):
         try:
             configure_rgw_credentials()
         except Exception as error:
-            logger.exception(error)
             return -errno.EINVAL, '', str(error)
 
         return 0, 'RGW credentials configured', ''
index 48e50c38d8f3b56173c576e167f5af29092f7527..b055946d09e3b13fb2b089ae1d9dedc0c8f1cace 100644 (file)
@@ -5,9 +5,9 @@ import ipaddress
 import json
 import logging
 import re
-import subprocess
 import xml.etree.ElementTree as ET  # noqa: N814
 from distutils.util import strtobool
+from subprocess import SubprocessError
 
 from .. import mgr
 from ..awsauth import S3Auth
@@ -29,7 +29,7 @@ class NoRgwDaemonsException(Exception):
         super().__init__('No RGW service is running.')
 
 
-class NoCredentialsException(RequestException):
+class NoCredentialsException(Exception):
     def __init__(self):
         super(NoCredentialsException, self).__init__(
             'No RGW credentials found, '
@@ -194,86 +194,76 @@ def _parse_frontend_config(config) -> Tuple[int, bool]:
     raise LookupError('Failed to determine RGW port from "{}"'.format(config))
 
 
-def _radosgw_admin(args: List[str]) -> Tuple[int, str, str]:
-    try:
-        result = subprocess.run(  # pylint: disable=subprocess-run-check
-            [
-                'radosgw-admin',
-                '-c', str(mgr.get_ceph_conf_path()),
-                '-k', str(mgr.get_ceph_option('keyring')),
-                '-n', f'mgr.{mgr.get_mgr_id()}',
-            ] + args,
-            stdout=subprocess.PIPE,
-            stderr=subprocess.PIPE,
-            timeout=10,
-        )
-        return result.returncode, result.stdout.decode('utf-8'), result.stderr.decode('utf-8')
-    except subprocess.CalledProcessError as ex:
-        mgr.log.error(f'Error executing radosgw-admin {ex.cmd}: {ex.output}')
-        raise
-    except subprocess.TimeoutExpired as ex:
-        mgr.log.error(f'Timeout (10s) executing radosgw-admin {ex.cmd}')
-        raise
-
-
-def _parse_secrets(user: str, payload: str) -> Tuple[Optional[str], Optional[str]]:
-    data = json.loads(payload)
+def _parse_secrets(user: str, data: dict) -> Tuple[str, str]:
     for key in data.get('keys', []):
         if key.get('user') == user and data.get('system') in ['true', True]:
             access_key = key.get('access_key')
             secret_key = key.get('secret_key')
             return access_key, secret_key
-    return None, None
+    return '', ''
 
 
 def _get_user_keys(user: str, realm: Optional[str] = None) -> Tuple[str, str]:
-    cmd = ['user', 'info', '--uid', user]
+    access_key = ''
+    secret_key = ''
+    rgw_user_info_cmd = ['user', 'info', '--uid', user]
     cmd_realm_option = ['--rgw-realm', realm] if realm else []
     if realm:
-        cmd += cmd_realm_option
-    rc, out, err = _radosgw_admin(cmd)
-    access_key = None
-    secret_key = None
-    if not rc:
-        access_key, secret_key = _parse_secrets(user, out)
-    if not access_key:
-        rgw_create_user_cmd = [
-            'user', 'create',
-            '--uid', user,
-            '--display-name', 'Ceph Dashboard',
-            '--system',
-        ] + cmd_realm_option
-        rc, out, err = _radosgw_admin(rgw_create_user_cmd)
-        if not rc:
+        rgw_user_info_cmd += cmd_realm_option
+    try:
+        _, out, err = mgr.send_rgwadmin_command(rgw_user_info_cmd)
+        if out:
             access_key, secret_key = _parse_secrets(user, out)
-    if not access_key:
-        mgr.log.error(f'Unable to create rgw {user} user: {err}')
-    assert access_key
-    assert secret_key
+        if not access_key:
+            rgw_create_user_cmd = [
+                'user', 'create',
+                '--uid', user,
+                '--display-name', 'Ceph Dashboard',
+                '--system',
+            ] + cmd_realm_option
+            _, out, err = mgr.send_rgwadmin_command(rgw_create_user_cmd)
+            if out:
+                access_key, secret_key = _parse_secrets(user, out)
+        if not access_key:
+            logger.error('Unable to create rgw user "%s": %s', user, err)
+    except SubprocessError as error:
+        logger.exception(error)
+
     return access_key, secret_key
 
 
 def configure_rgw_credentials():
-    mgr.log.info('Configuring dashboard RGW credentials')
+    logger.info('Configuring dashboard RGW credentials')
     user = 'dashboard'
-    rc, out, err = _radosgw_admin(['realm', 'list'])
-    if rc:
-        error = RgwAdminException(f'Unable to list RGW realms: {err}')
-        mgr.log.exception(error)
-        raise error
-    j = json.loads(out)
-    realms = j.get('realms', [])
-    if realms:
-        access_keys = {}
-        secret_keys = {}
-        for realm in realms:
-            access_keys[realm], secret_keys[realm] = _get_user_keys(user, realm)
-        access_key = json.dumps(access_keys)
-        secret_key = json.dumps(secret_keys)
-    else:
-        access_key, secret_key = _get_user_keys(user)
-    Settings.RGW_API_ACCESS_KEY = access_key
-    Settings.RGW_API_SECRET_KEY = secret_key
+    realms = []
+    access_key = ''
+    secret_key = ''
+    try:
+        _, out, err = mgr.send_rgwadmin_command(['realm', 'list'])
+        if out:
+            realms = out.get('realms', [])
+        if err:
+            logger.error('Unable to list RGW realms: %s', err)
+        if realms:
+            realm_access_keys = {}
+            realm_secret_keys = {}
+            for realm in realms:
+                realm_access_key, realm_secret_key = _get_user_keys(user, realm)
+                if realm_access_key:
+                    realm_access_keys[realm] = realm_access_key
+                    realm_secret_keys[realm] = realm_secret_key
+            if realm_access_keys:
+                access_key = json.dumps(realm_access_keys)
+                secret_key = json.dumps(realm_secret_keys)
+        else:
+            access_key, secret_key = _get_user_keys(user)
+
+        assert access_key and secret_key
+        Settings.RGW_API_ACCESS_KEY = access_key
+        Settings.RGW_API_SECRET_KEY = secret_key
+    except (AssertionError, SubprocessError) as error:
+        logger.exception(error)
+        raise NoCredentialsException
 
 
 class RgwClient(RestClient):
@@ -318,12 +308,9 @@ class RgwClient(RestClient):
 
     @staticmethod
     def _rgw_settings():
-        return (Settings.RGW_API_HOST,
-                Settings.RGW_API_PORT,
-                Settings.RGW_API_ACCESS_KEY,
+        return (Settings.RGW_API_ACCESS_KEY,
                 Settings.RGW_API_SECRET_KEY,
                 Settings.RGW_API_ADMIN_RESOURCE,
-                Settings.RGW_API_SCHEME,
                 Settings.RGW_API_SSL_VERIFY)
 
     @staticmethod
@@ -335,30 +322,11 @@ class RgwClient(RestClient):
 
         # The API access key and secret key are mandatory for a minimal configuration.
         if not (Settings.RGW_API_ACCESS_KEY and Settings.RGW_API_SECRET_KEY):
-            try:
-                configure_rgw_credentials()
-            except Exception as error:
-                logger.exception(error)
-                raise NoCredentialsException()
+            configure_rgw_credentials()
 
         if not daemon_name:
-            # Select default daemon if configured in settings:
-            if Settings.RGW_API_HOST and Settings.RGW_API_PORT:
-                for daemon in RgwClient._daemons.values():
-                    if daemon.host == Settings.RGW_API_HOST \
-                            and daemon.port == Settings.RGW_API_PORT:
-                        daemon_name = daemon.name
-                        break
-                if not daemon_name:
-                    raise DashboardException(
-                        msg='No RGW daemon found with user-defined host: {}, port: {}'.format(
-                            Settings.RGW_API_HOST,
-                            Settings.RGW_API_PORT),
-                        http_status_code=404,
-                        component='rgw')
             # Select 1st daemon:
-            else:
-                daemon_name = next(iter(RgwClient._daemons.keys()))
+            daemon_name = next(iter(RgwClient._daemons.keys()))
 
         # Discard all cached instances if any rgw setting has changed
         if RgwClient._rgw_settings_snapshot != RgwClient._rgw_settings():
@@ -444,6 +412,7 @@ class RgwClient(RestClient):
             self.userid = self._get_user_id(self.admin_path) if self.got_keys_from_config \
                 else user_id
         except RequestException as error:
+            logger.exception(error)
             msg = 'Error connecting to Object Gateway'
             if error.status_code == 404:
                 msg = '{}: {}'.format(msg, str(error))
index d76710a98e6e2cffabdcf01e0a5fb237c387b33e..3b4a8e4eedc94823f71db283a38b471cd76d87a2 100644 (file)
@@ -65,13 +65,9 @@ class Options(object):
     AUDIT_API_LOG_PAYLOAD = Setting(True, [bool])
 
     # RGW settings
-    RGW_API_HOST = Setting('', [dict, str])
-    RGW_API_PORT = Setting(80, [dict, int])
     RGW_API_ACCESS_KEY = Setting('', [dict, str])
     RGW_API_SECRET_KEY = Setting('', [dict, str])
     RGW_API_ADMIN_RESOURCE = Setting('admin', [str])
-    RGW_API_SCHEME = Setting('http', [str])
-    RGW_API_USER_ID = Setting('', [dict, str])
     RGW_API_SSL_VERIFY = Setting(True, [bool])
 
     # Grafana settings
index 59233bf4f5c3d907e55efbb86bcc2be62b133724..9075cdc65fc857d27e14e73bdb1cd89b126d0c78 100644 (file)
@@ -322,8 +322,6 @@ class RgwStub(Stub):
     @classmethod
     def get_settings(cls):
         settings = {
-            'RGW_API_HOST': '',
-            'RGW_API_PORT': 0,
             'RGW_API_ACCESS_KEY': 'fake-access-key',
             'RGW_API_SECRET_KEY': 'fake-secret-key',
         }
index ff75fcf71c9a8a1af248c2ed34f75ced59e3302e..f8a8f2f689d2ebab6e274700561015d9b9f01bfe 100644 (file)
@@ -4,11 +4,12 @@ import errno
 from unittest import TestCase
 from unittest.mock import Mock, patch
 
+from .. import mgr
 from ..exceptions import DashboardException
 from ..services.rgw_client import NoCredentialsException, \
     NoRgwDaemonsException, RgwClient, _parse_frontend_config
 from ..settings import Settings
-from . import CLICommandTestMixin, CmdException, RgwStub  # pylint: disable=no-name-in-module
+from . import CLICommandTestMixin, RgwStub  # pylint: disable=no-name-in-module
 
 
 @patch('dashboard.services.rgw_client.RgwClient._get_user_id', Mock(
@@ -19,19 +20,33 @@ class RgwClientTest(TestCase, CLICommandTestMixin):
     _dashboard_user_realm2_access_key = 'OMDR282VYLBC1ZYMYDL0'
     _dashboard_user_realm2_secret_key = 'N3thf7jAiwQ90PsPrhC2DIcvCFOsBXtBvPJJMdC3'
     _radosgw_admin_result_error = (-errno.EINVAL, '', 'fake error')
-    _radosgw_admin_result_no_realms = (0, '{}', '')
-    _radosgw_admin_result_realms = (0, '{"realms": ["realm1", "realm2"]}', '')
-    _radosgw_admin_result_user_realm1_payload = (
+    _radosgw_admin_result_no_realms = (0, {}, '')
+    _radosgw_admin_result_realms = (0, {"realms": ["realm1", "realm2"]}, '')
+    _radosgw_admin_result_user_realm1 = (
         0,
-        '{"keys": [{"user": "dashboard", "access_key": "'
-        f'{_dashboard_user_realm1_access_key}",'
-        f'"secret_key": "{_dashboard_user_realm1_secret_key}"}}], "system": "true"}}',
+        {
+            "keys": [
+                {
+                    "user": "dashboard",
+                    "access_key": _dashboard_user_realm1_access_key,
+                    "secret_key": _dashboard_user_realm1_secret_key
+                }
+            ],
+            "system": "true"
+        },
         '')
-    _radosgw_admin_result_user_realm2_payload = (
+    _radosgw_admin_result_user_realm2 = (
         0,
-        '{"keys": [{"user": "dashboard", "access_key": "'
-        f'{_dashboard_user_realm2_access_key}",'
-        f'"secret_key": "{_dashboard_user_realm2_secret_key}"}}], "system": "true"}}',
+        {
+            "keys": [
+                {
+                    "user": "dashboard",
+                    "access_key": _dashboard_user_realm2_access_key,
+                    "secret_key": _dashboard_user_realm2_secret_key
+                }
+            ],
+            "system": "true"
+        },
         '')
 
     def setUp(self):
@@ -42,58 +57,87 @@ class RgwClientTest(TestCase, CLICommandTestMixin):
             'RGW_API_SECRET_KEY': 'supergeheim',
         })
 
-    @patch('dashboard.services.rgw_client._radosgw_admin')
-    def test_connect_rgw(self, radosgw_admin):
-        radosgw_admin.side_effect = [
-            self._radosgw_admin_result_error,
-            self._radosgw_admin_result_no_realms,
-            self._radosgw_admin_result_user_realm1_payload
-        ]
-        with self.assertRaises(CmdException) as ctx:
-            self.exec_cmd('connect-rgw')
-        self.assertEqual(ctx.exception.retcode, -errno.EINVAL)
-        self.assertIn('Unable to list RGW realms', str(ctx.exception))
-
-        result = self.exec_cmd('connect-rgw')
-        self.assertEqual(result, 'RGW credentials configured')
-        self.assertEqual(Settings.RGW_API_ACCESS_KEY, self._dashboard_user_realm1_access_key)
-        self.assertEqual(Settings.RGW_API_SECRET_KEY, self._dashboard_user_realm1_secret_key)
-
     def test_configure_credentials_error(self):
         self.CONFIG_KEY_DICT.update({
             'RGW_API_ACCESS_KEY': '',
             'RGW_API_SECRET_KEY': '',
         })
+        # Get no realms, get no user, user creation fails.
+        mgr.send_rgwadmin_command.side_effect = [
+            self._radosgw_admin_result_error,
+            self._radosgw_admin_result_error,
+            self._radosgw_admin_result_error,
+        ]
         with self.assertRaises(NoCredentialsException) as cm:
             RgwClient.admin_instance()
         self.assertIn('No RGW credentials found', str(cm.exception))
 
-    @patch('dashboard.services.rgw_client._radosgw_admin')
-    def test_configure_credentials_with_no_realms(self, radosgw_admin):
+    def test_configure_credentials_error_with_realms(self):
         self.CONFIG_KEY_DICT.update({
             'RGW_API_ACCESS_KEY': '',
             'RGW_API_SECRET_KEY': '',
         })
-        radosgw_admin.side_effect = [
-            self._radosgw_admin_result_no_realms,
-            self._radosgw_admin_result_user_realm1_payload
+        # Get realms, get no user, user creation fails.
+        mgr.send_rgwadmin_command.side_effect = [
+            self._radosgw_admin_result_realms,
+            self._radosgw_admin_result_error,
+            self._radosgw_admin_result_error,
+            self._radosgw_admin_result_error,
+            self._radosgw_admin_result_error,
+        ]
+        with self.assertRaises(NoCredentialsException) as cm:
+            RgwClient.admin_instance()
+        self.assertIn('No RGW credentials found', str(cm.exception))
+
+    def test_set_rgw_credentials_command(self):
+        # Get no realms, get user.
+        mgr.send_rgwadmin_command.side_effect = [
+            self._radosgw_admin_result_error,
+            self._radosgw_admin_result_user_realm1
         ]
-        RgwClient.admin_instance()
+        result = self.exec_cmd('set-rgw-credentials')
+        self.assertEqual(result, 'RGW credentials configured')
         self.assertEqual(Settings.RGW_API_ACCESS_KEY, self._dashboard_user_realm1_access_key)
         self.assertEqual(Settings.RGW_API_SECRET_KEY, self._dashboard_user_realm1_secret_key)
 
-    @patch('dashboard.services.rgw_client._radosgw_admin')
-    def test_configure_credentials_with_realms(self, radosgw_admin):
-        self.CONFIG_KEY_DICT.update({
-            'RGW_API_ACCESS_KEY': '',
-            'RGW_API_SECRET_KEY': '',
+        # Get no realms, get no user, user creation.
+        mgr.send_rgwadmin_command.side_effect = [
+            self._radosgw_admin_result_error,
+            self._radosgw_admin_result_error,
+            self._radosgw_admin_result_user_realm1
+        ]
+        result = self.exec_cmd('set-rgw-credentials')
+        self.assertEqual(result, 'RGW credentials configured')
+        self.assertEqual(Settings.RGW_API_ACCESS_KEY, self._dashboard_user_realm1_access_key)
+        self.assertEqual(Settings.RGW_API_SECRET_KEY, self._dashboard_user_realm1_secret_key)
+
+        # Get realms, get users.
+        mgr.send_rgwadmin_command.side_effect = [
+            self._radosgw_admin_result_realms,
+            self._radosgw_admin_result_user_realm1,
+            self._radosgw_admin_result_user_realm2
+        ]
+        result = self.exec_cmd('set-rgw-credentials')
+        self.assertEqual(result, 'RGW credentials configured')
+        self.assertEqual(Settings.RGW_API_ACCESS_KEY, {
+            'realm1': self._dashboard_user_realm1_access_key,
+            'realm2': self._dashboard_user_realm2_access_key
         })
-        radosgw_admin.side_effect = [
+        self.assertEqual(Settings.RGW_API_SECRET_KEY, {
+            'realm1': self._dashboard_user_realm1_secret_key,
+            'realm2': self._dashboard_user_realm2_secret_key
+        })
+
+        # Get realms, get no users, users' creation.
+        mgr.send_rgwadmin_command.side_effect = [
             self._radosgw_admin_result_realms,
-            self._radosgw_admin_result_user_realm1_payload,
-            self._radosgw_admin_result_user_realm2_payload
+            self._radosgw_admin_result_error,
+            self._radosgw_admin_result_user_realm1,
+            self._radosgw_admin_result_error,
+            self._radosgw_admin_result_user_realm2
         ]
-        RgwClient.admin_instance()
+        result = self.exec_cmd('set-rgw-credentials')
+        self.assertEqual(result, 'RGW credentials configured')
         self.assertEqual(Settings.RGW_API_ACCESS_KEY, {
             'realm1': self._dashboard_user_realm1_access_key,
             'realm2': self._dashboard_user_realm2_access_key
@@ -103,6 +147,23 @@ class RgwClientTest(TestCase, CLICommandTestMixin):
             'realm2': self._dashboard_user_realm2_secret_key
         })
 
+        # Get realms, get no users, realm 2 user creation fails.
+        mgr.send_rgwadmin_command.side_effect = [
+            self._radosgw_admin_result_realms,
+            self._radosgw_admin_result_error,
+            self._radosgw_admin_result_user_realm1,
+            self._radosgw_admin_result_error,
+            self._radosgw_admin_result_error,
+        ]
+        result = self.exec_cmd('set-rgw-credentials')
+        self.assertEqual(result, 'RGW credentials configured')
+        self.assertEqual(Settings.RGW_API_ACCESS_KEY, {
+            'realm1': self._dashboard_user_realm1_access_key,
+        })
+        self.assertEqual(Settings.RGW_API_SECRET_KEY, {
+            'realm1': self._dashboard_user_realm1_secret_key,
+        })
+
     def test_ssl_verify(self):
         Settings.RGW_API_SSL_VERIFY = True
         instance = RgwClient.admin_instance()
@@ -119,15 +180,6 @@ class RgwClientTest(TestCase, CLICommandTestMixin):
             RgwClient.admin_instance()
         self.assertIn('No RGW service is running.', str(cm.exception))
 
-    def test_default_daemon_wrong_settings(self):
-        self.CONFIG_KEY_DICT.update({
-            'RGW_API_HOST': '172.20.0.2',
-            'RGW_API_PORT': '7990',
-        })
-        with self.assertRaises(DashboardException) as cm:
-            RgwClient.admin_instance()
-        self.assertIn('No RGW daemon found with user-defined host:', str(cm.exception))
-
     @patch.object(RgwClient, '_get_daemon_zone_info')
     def test_get_placement_targets_from_zone(self, zone_info):
         zone_info.return_value = {
index 593db937645b23e00bee8cf7cea8dbac4483605b..ecc46fe8f969971597ff13f748a48e233993a65d 100644 (file)
@@ -14,6 +14,7 @@ import logging
 import errno
 import functools
 import json
+import subprocess
 import threading
 from collections import defaultdict
 from enum import IntEnum
@@ -1838,3 +1839,33 @@ class MgrModule(ceph_module.BaseMgrModule, MgrModuleLoggingMixin):
         :param arguments: dict of key/value arguments to test
         """
         return self._ceph_is_authorized(arguments)
+
+    def send_rgwadmin_command(self, args: List[str],
+                              stdout_as_json: bool = True) -> Tuple[int, Union[str, dict], str]:
+        try:
+            cmd = [
+                    'radosgw-admin',
+                    '-c', str(self.get_ceph_conf_path()),
+                    '-k', str(self.get_ceph_option('keyring')),
+                    '-n', f'mgr.{self.get_mgr_id()}',
+                ] + args
+            self.log.debug('Executing %s', str(cmd))
+            result = subprocess.run(  # pylint: disable=subprocess-run-check
+                cmd,
+                stdout=subprocess.PIPE,
+                stderr=subprocess.PIPE,
+                timeout=10,
+            )
+            stdout = result.stdout.decode('utf-8')
+            stderr = result.stderr.decode('utf-8')
+            if stdout and stdout_as_json:
+                stdout = json.loads(stdout)
+            if result.returncode:
+                self.log.debug('Error %s executing %s: %s', result.returncode, str(cmd), stderr)
+            return result.returncode, stdout, stderr
+        except subprocess.CalledProcessError as ex:
+            self.log.exception('Error executing radosgw-admin %s: %s', str(ex.cmd), str(ex.output))
+            raise
+        except subprocess.TimeoutExpired as ex:
+            self.log.error('Timeout (10s) executing radosgw-admin %s', str(ex.cmd))
+            raise