from __future__ import absolute_import
import ipaddress
+import json
import logging
import re
import xml.etree.ElementTree as ET # noqa: N814
from distutils.util import strtobool
+from subprocess import SubprocessError
+
+from mgr_util import build_url
from .. import mgr
from ..awsauth import S3Auth
from ..exceptions import DashboardException
from ..rest_client import RequestException, RestClient
from ..settings import Settings
-from ..tools import build_url, dict_contains_path, dict_get, json_str_to_object
+from ..tools import dict_contains_path, dict_get, json_str_to_object
try:
from typing import Any, Dict, List, Optional, Tuple, Union
super().__init__('No RGW service is running.')
-class NoCredentialsException(RequestException):
+class NoCredentialsException(Exception):
def __init__(self):
super(NoCredentialsException, self).__init__(
'No RGW credentials found, '
'the dashboard.')
+class RgwAdminException(Exception):
+ pass
+
+
class RgwDaemon:
"""Simple representation of a daemon."""
host: str
name: str
port: int
ssl: bool
+ realm_name: str
zonegroup_name: str
+ zone_name: str
def _get_daemons() -> Dict[str, RgwDaemon]:
if dict_contains_path(daemon_map[key], ['metadata', 'frontend_config#0']):
daemon = _determine_rgw_addr(daemon_map[key])
daemon.name = daemon_map[key]['metadata']['id']
+ daemon.realm_name = daemon_map[key]['metadata']['realm_name']
daemon.zonegroup_name = daemon_map[key]['metadata']['zonegroup_name']
+ daemon.zone_name = daemon_map[key]['metadata']['zone_name']
daemons[daemon.name] = daemon
logger.info('Found RGW daemon with configuration: host=%s, port=%d, ssl=%s',
daemon.host, daemon.port, str(daemon.ssl))
raise LookupError('Failed to determine RGW port from "{}"'.format(config))
+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 '', ''
+
+
+def _get_user_keys(user: str, realm: Optional[str] = None) -> Tuple[str, str]:
+ access_key = ''
+ secret_key = ''
+ rgw_user_info_cmd = ['user', 'info', '--uid', user]
+ cmd_realm_option = ['--rgw-realm', realm] if realm else []
+ if realm:
+ 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:
+ 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():
+ logger.info('Configuring dashboard RGW credentials')
+ user = 'dashboard'
+ 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):
_host = None
_port = None
@staticmethod
def _get_daemon_connection_info(daemon_name: str) -> dict:
try:
- access_key = Settings.RGW_API_ACCESS_KEY[daemon_name]
- secret_key = Settings.RGW_API_SECRET_KEY[daemon_name]
+ realm_name = RgwClient._daemons[daemon_name].realm_name
+ access_key = Settings.RGW_API_ACCESS_KEY[realm_name]
+ secret_key = Settings.RGW_API_SECRET_KEY[realm_name]
except TypeError:
# Legacy string values.
access_key = Settings.RGW_API_ACCESS_KEY
@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):
- logger.warning('No credentials found, please consult the '
- 'documentation about how to enable RGW for the '
- 'dashboard.')
- 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():
service_url=self.service_url)
def __init__(self,
- access_key,
- secret_key,
- daemon_name,
- user_id=None):
+ access_key: str,
+ secret_key: str,
+ daemon_name: str,
+ user_id: Optional[str] = None) -> None:
try:
daemon = RgwClient._daemons[daemon_name]
except KeyError as error:
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))