import json
import logging
+import re
from typing import Any, Dict, List, NamedTuple, Optional, Union
import cherrypy
+from .. import mgr
from ..exceptions import DashboardException
from ..rest_client import RequestException
from ..security import Permission, Scope
from ..services.auth import AuthManager, JwtManager
from ..services.ceph_service import CephService
-from ..services.rgw_client import NoRgwDaemonsException, RgwClient
+from ..services.rgw_client import NoRgwDaemonsException, RgwClient, RgwMultisite
from ..tools import json_str_to_object, str_to_bool
-from . import APIDoc, APIRouter, BaseController, CRUDCollectionMethod, \
- CRUDEndpoint, Endpoint, EndpointDoc, ReadPermission, RESTController, \
- UIRouter, allow_empty_body
+from . import APIDoc, APIRouter, BaseController, CreatePermission, \
+ CRUDCollectionMethod, CRUDEndpoint, Endpoint, EndpointDoc, ReadPermission, \
+ RESTController, UIRouter, UpdatePermission, allow_empty_body
from ._crud import CRUDMeta, Form, FormField, FormTaskInfo, Icon, MethodType, \
TableAction, Validator, VerticalContainer
from ._version import APIVersion
return status
+@UIRouter('/rgw/multisite')
+class RgwMultisiteStatus(RESTController):
+ @Endpoint()
+ @ReadPermission
+ # pylint: disable=R0801
+ def status(self):
+ status = {'available': True, 'message': None}
+ multisite_instance = RgwMultisite()
+ is_multisite_configured = multisite_instance.get_multisite_status()
+ if not is_multisite_configured:
+ status['available'] = False
+ status['message'] = 'Multi-site provides disaster recovery and may also \
+ serve as a foundation for content delivery networks' # type: ignore
+ return status
+
+ @RESTController.Collection(method='PUT', path='/migrate')
+ @allow_empty_body
+ # pylint: disable=W0102,W0613
+ def migrate(self, daemon_name=None, realm_name=None, zonegroup_name=None, zone_name=None,
+ zonegroup_endpoints=None, zone_endpoints=None, access_key=None,
+ secret_key=None):
+ multisite_instance = RgwMultisite()
+ result = multisite_instance.migrate_to_multisite(realm_name, zonegroup_name,
+ zone_name, zonegroup_endpoints,
+ zone_endpoints, access_key,
+ secret_key)
+ return result
+
+ @RESTController.Collection(method='GET', path='/sync_status')
+ @allow_empty_body
+ # pylint: disable=W0102,W0613
+ def get_sync_status(self):
+ multisite_instance = RgwMultisite()
+ result = multisite_instance.get_multisite_sync_status()
+ return result
+
+
@APIRouter('/rgw/daemon', Scope.RGW)
@APIDoc("RGW Daemon Management API", "RgwDaemon")
class RgwDaemon(RESTController):
'zonegroup_name': metadata['zonegroup_name'],
'zone_name': metadata['zone_name'],
'default': instance.daemon.name == metadata['id'],
- 'port': int(metadata['frontend_config#0'].split('port=')[1])
+ 'port': int(re.findall(r'port=(\d+)', metadata['frontend_config#0'])[0])
}
daemons.append(daemon)
daemon['rgw_status'] = status
return daemon
+ @RESTController.Collection(method='PUT', path='/set_multisite_config')
+ @allow_empty_body
+ def set_multisite_config(self, realm_name=None, zonegroup_name=None,
+ zone_name=None, daemon_name=None):
+ CephService.set_multisite_config(realm_name, zonegroup_name, zone_name, daemon_name)
+
class RgwRESTController(RESTController):
def proxy(self, daemon_name, method, path, params=None, json_response=True):
return CephService.get_encryption_config(daemon_name)
+@UIRouter('/rgw/bucket', Scope.RGW)
+class RgwBucketUi(RgwBucket):
+ @Endpoint('GET')
+ @ReadPermission
+ # pylint: disable=W0613
+ def buckets_and_users_count(self, daemon_name=None):
+ buckets_count = 0
+ users_count = 0
+ daemon_object = RgwDaemon()
+ daemons = json.loads(daemon_object.list())
+ unique_realms = set()
+ for daemon in daemons:
+ realm_name = daemon.get('realm_name', None)
+ if realm_name:
+ if realm_name not in unique_realms:
+ unique_realms.add(realm_name)
+ buckets = json.loads(RgwBucket.list(self, daemon_name=daemon['id']))
+ users = json.loads(RgwUser.list(self, daemon_name=daemon['id']))
+ users_count += len(users)
+ buckets_count += len(buckets)
+ else:
+ buckets = json.loads(RgwBucket.list(self, daemon_name=daemon['id']))
+ users = json.loads(RgwUser.list(self, daemon_name=daemon['id']))
+ users_count = len(users)
+ buckets_count = len(buckets)
+
+ return {
+ 'buckets_count': buckets_count,
+ 'users_count': users_count
+ }
+
+
@APIRouter('/rgw/user', Scope.RGW)
@APIDoc("RGW User Management API", "RgwUser")
class RgwUser(RgwRESTController):
CreateDate: str
MaxSessionDuration: int
AssumeRolePolicyDocument: str
+
+
+@APIRouter('/rgw/realm', Scope.RGW)
+class RgwRealm(RESTController):
+ @allow_empty_body
+ # pylint: disable=W0613
+ def create(self, realm_name, default):
+ multisite_instance = RgwMultisite()
+ result = multisite_instance.create_realm(realm_name, default)
+ return result
+
+ @allow_empty_body
+ # pylint: disable=W0613
+ def list(self):
+ multisite_instance = RgwMultisite()
+ result = multisite_instance.list_realms()
+ return result
+
+ @allow_empty_body
+ # pylint: disable=W0613
+ def get(self, realm_name):
+ multisite_instance = RgwMultisite()
+ result = multisite_instance.get_realm(realm_name)
+ return result
+
+ @Endpoint()
+ @ReadPermission
+ def get_all_realms_info(self):
+ multisite_instance = RgwMultisite()
+ result = multisite_instance.get_all_realms_info()
+ return result
+
+ @allow_empty_body
+ # pylint: disable=W0613
+ def set(self, realm_name: str, new_realm_name: str, default: str = ''):
+ multisite_instance = RgwMultisite()
+ result = multisite_instance.edit_realm(realm_name, new_realm_name, default)
+ return result
+
+ @Endpoint()
+ @ReadPermission
+ def get_realm_tokens(self):
+ try:
+ result = CephService.get_realm_tokens()
+ return result
+ except NoRgwDaemonsException as e:
+ raise DashboardException(e, http_status_code=404, component='rgw')
+
+ @Endpoint(method='POST')
+ @UpdatePermission
+ @allow_empty_body
+ # pylint: disable=W0613
+ def import_realm_token(self, realm_token, zone_name, port, placement_spec):
+ try:
+ multisite_instance = RgwMultisite()
+ result = CephService.import_realm_token(realm_token, zone_name, port, placement_spec)
+ multisite_instance.update_period()
+ return result
+ except NoRgwDaemonsException as e:
+ raise DashboardException(e, http_status_code=404, component='rgw')
+
+ def delete(self, realm_name):
+ multisite_instance = RgwMultisite()
+ result = multisite_instance.delete_realm(realm_name)
+ return result
+
+
+@APIRouter('/rgw/zonegroup', Scope.RGW)
+class RgwZonegroup(RESTController):
+ @allow_empty_body
+ # pylint: disable=W0613
+ def create(self, realm_name, zonegroup_name, default=None, master=None,
+ zonegroup_endpoints=None):
+ multisite_instance = RgwMultisite()
+ result = multisite_instance.create_zonegroup(realm_name, zonegroup_name, default,
+ master, zonegroup_endpoints)
+ return result
+
+ @allow_empty_body
+ # pylint: disable=W0613
+ def list(self):
+ multisite_instance = RgwMultisite()
+ result = multisite_instance.list_zonegroups()
+ return result
+
+ @allow_empty_body
+ # pylint: disable=W0613
+ def get(self, zonegroup_name):
+ multisite_instance = RgwMultisite()
+ result = multisite_instance.get_zonegroup(zonegroup_name)
+ return result
+
+ @Endpoint()
+ @ReadPermission
+ def get_all_zonegroups_info(self):
+ multisite_instance = RgwMultisite()
+ result = multisite_instance.get_all_zonegroups_info()
+ return result
+
+ def delete(self, zonegroup_name, delete_pools, pools: Optional[List[str]] = None):
+ if pools is None:
+ pools = []
+ try:
+ multisite_instance = RgwMultisite()
+ result = multisite_instance.delete_zonegroup(zonegroup_name, delete_pools, pools)
+ return result
+ except NoRgwDaemonsException as e:
+ raise DashboardException(e, http_status_code=404, component='rgw')
+
+ @allow_empty_body
+ # pylint: disable=W0613,W0102
+ def set(self, zonegroup_name: str, realm_name: str, new_zonegroup_name: str,
+ default: str = '', master: str = '', zonegroup_endpoints: str = '',
+ add_zones: List[str] = [], remove_zones: List[str] = [],
+ placement_targets: List[Dict[str, str]] = []):
+ multisite_instance = RgwMultisite()
+ result = multisite_instance.edit_zonegroup(realm_name, zonegroup_name, new_zonegroup_name,
+ default, master, zonegroup_endpoints, add_zones,
+ remove_zones, placement_targets)
+ return result
+
+
+@APIRouter('/rgw/zone', Scope.RGW)
+class RgwZone(RESTController):
+ @allow_empty_body
+ # pylint: disable=W0613
+ def create(self, zone_name, zonegroup_name=None, default=False, master=False,
+ zone_endpoints=None, access_key=None, secret_key=None):
+ multisite_instance = RgwMultisite()
+ result = multisite_instance.create_zone(zone_name, zonegroup_name, default,
+ master, zone_endpoints, access_key,
+ secret_key)
+ return result
+
+ @allow_empty_body
+ # pylint: disable=W0613
+ def list(self):
+ multisite_instance = RgwMultisite()
+ result = multisite_instance.list_zones()
+ return result
+
+ @allow_empty_body
+ # pylint: disable=W0613
+ def get(self, zone_name):
+ multisite_instance = RgwMultisite()
+ result = multisite_instance.get_zone(zone_name)
+ return result
+
+ @Endpoint()
+ @ReadPermission
+ def get_all_zones_info(self):
+ multisite_instance = RgwMultisite()
+ result = multisite_instance.get_all_zones_info()
+ return result
+
+ def delete(self, zone_name, delete_pools, pools: Optional[List[str]] = None,
+ zonegroup_name=None):
+ if pools is None:
+ pools = []
+ if zonegroup_name is None:
+ zonegroup_name = ''
+ try:
+ multisite_instance = RgwMultisite()
+ result = multisite_instance.delete_zone(zone_name, delete_pools, pools, zonegroup_name)
+ return result
+ except NoRgwDaemonsException as e:
+ raise DashboardException(e, http_status_code=404, component='rgw')
+
+ @allow_empty_body
+ # pylint: disable=W0613,W0102
+ def set(self, zone_name: str, new_zone_name: str, zonegroup_name: str, default: str = '',
+ master: str = '', zone_endpoints: str = '', access_key: str = '', secret_key: str = '',
+ placement_target: str = '', data_pool: str = '', index_pool: str = '',
+ data_extra_pool: str = '', storage_class: str = '', data_pool_class: str = '',
+ compression: str = ''):
+ multisite_instance = RgwMultisite()
+ result = multisite_instance.edit_zone(zone_name, new_zone_name, zonegroup_name, default,
+ master, zone_endpoints, access_key, secret_key,
+ placement_target, data_pool, index_pool,
+ data_extra_pool, storage_class, data_pool_class,
+ compression)
+ return result
+
+ @Endpoint()
+ @ReadPermission
+ def get_pool_names(self):
+ pool_names = []
+ ret, out, _ = mgr.check_mon_command({
+ 'prefix': 'osd lspools',
+ 'format': 'json',
+ })
+ if ret == 0 and out is not None:
+ pool_names = json.loads(out)
+ return pool_names
+
+ @Endpoint('PUT')
+ @CreatePermission
+ def create_system_user(self, userName: str, zoneName: str):
+ multisite_instance = RgwMultisite()
+ result = multisite_instance.create_system_user(userName, zoneName)
+ return result
+
+ @Endpoint()
+ @ReadPermission
+ def get_user_list(self, zoneName=None):
+ multisite_instance = RgwMultisite()
+ result = multisite_instance.get_user_list(zoneName)
+ return result