^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
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
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)
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)
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()
it('should test editing on dashboard module', () => {
const dashboardArr: Input[] = [
- {
- id: 'RGW_API_USER_ID',
- newValue: 'rq',
- oldValue: ''
- },
{
id: 'GRAFANA_API_PASSWORD',
newValue: 'rafa',
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', ''
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
super().__init__('No RGW service is running.')
-class NoCredentialsException(RequestException):
+class NoCredentialsException(Exception):
def __init__(self):
super(NoCredentialsException, self).__init__(
'No RGW credentials found, '
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):
@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
# 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():
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))
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
@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',
}
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(
_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):
'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
'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()
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 = {
import errno
import functools
import json
+import subprocess
import threading
from collections import defaultdict
from enum import IntEnum
: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