]>
git.proxmox.com Git - ceph.git/blob - ceph/src/pybind/mgr/dashboard/controllers/rgw.py
1 # -*- coding: utf-8 -*-
2 from __future__
import absolute_import
8 from . import ApiController
, BaseController
, RESTController
, Endpoint
, \
11 from ..exceptions
import DashboardException
12 from ..rest_client
import RequestException
13 from ..security
import Scope
14 from ..services
.ceph_service
import CephService
15 from ..services
.rgw_client
import RgwClient
18 @ApiController('/rgw', Scope
.RGW
)
19 class Rgw(BaseController
):
24 status
= {'available': False, 'message': None}
26 instance
= RgwClient
.admin_instance()
27 # Check if the service is online.
28 if not instance
.is_service_online():
29 msg
= 'Failed to connect to the Object Gateway\'s Admin Ops API.'
30 raise RequestException(msg
)
31 # Ensure the API user ID is known by the RGW.
32 if not instance
.user_exists():
33 msg
= 'The user "{}" is unknown to the Object Gateway.'.format(
35 raise RequestException(msg
)
36 # Ensure the system flag is set for the API user ID.
37 if not instance
.is_system_user():
38 msg
= 'The system flag is not set for user "{}".'.format(
40 raise RequestException(msg
)
41 status
['available'] = True
42 except (RequestException
, LookupError) as ex
:
43 status
['message'] = str(ex
)
47 @ApiController('/rgw/daemon', Scope
.RGW
)
48 class RgwDaemon(RESTController
):
52 for hostname
, server
in CephService
.get_service_map('rgw').items():
53 for service
in server
['services']:
54 metadata
= service
['metadata']
56 # extract per-daemon service data and health
59 'version': metadata
['ceph_version'],
60 'server_hostname': hostname
63 daemons
.append(daemon
)
65 return sorted(daemons
, key
=lambda k
: k
['id'])
67 def get(self
, svc_id
):
73 service
= CephService
.get_service('rgw', svc_id
)
75 raise cherrypy
.NotFound('Service rgw {} is not available'.format(svc_id
))
77 metadata
= service
['metadata']
78 status
= service
['status']
81 status
= json
.loads(status
['json'])
83 logger
.warning('%s had invalid status json', service
['id'])
86 logger
.warning('%s has no key "json" in status', service
['id'])
88 daemon
['rgw_metadata'] = metadata
89 daemon
['rgw_status'] = status
93 class RgwRESTController(RESTController
):
95 def proxy(self
, method
, path
, params
=None, json_response
=True):
97 instance
= RgwClient
.admin_instance()
98 result
= instance
.proxy(method
, path
, params
, None)
99 if json_response
and result
!= '':
100 result
= json
.loads(result
.decode('utf-8'))
102 except (DashboardException
, RequestException
) as e
:
103 raise DashboardException(e
, http_status_code
=500, component
='rgw')
106 @ApiController('/rgw/bucket', Scope
.RGW
)
107 class RgwBucket(RgwRESTController
):
109 def _append_bid(self
, bucket
):
111 Append the bucket identifier that looks like [<tenant>/]<bucket>.
112 See http://docs.ceph.com/docs/nautilus/radosgw/multitenancy/ for
114 :param bucket: The bucket parameters.
116 :return: The modified bucket parameters including the 'bid' parameter.
119 if isinstance(bucket
, dict):
120 bucket
['bid'] = '{}/{}'.format(bucket
['tenant'], bucket
['bucket']) \
121 if bucket
['tenant'] else bucket
['bucket']
125 return self
.proxy('GET', 'bucket')
127 def get(self
, bucket
):
128 result
= self
.proxy('GET', 'bucket', {'bucket': bucket
})
129 return self
._append
_bid
(result
)
131 def create(self
, bucket
, uid
):
133 rgw_client
= RgwClient
.instance(uid
)
134 return rgw_client
.create_bucket(bucket
)
135 except RequestException
as e
:
136 raise DashboardException(e
, http_status_code
=500, component
='rgw')
138 def set(self
, bucket
, bucket_id
, uid
):
139 result
= self
.proxy('PUT', 'bucket', {
141 'bucket-id': bucket_id
,
143 }, json_response
=False)
144 return self
._append
_bid
(result
)
146 def delete(self
, bucket
, purge_objects
='true'):
147 return self
.proxy('DELETE', 'bucket', {
149 'purge-objects': purge_objects
150 }, json_response
=False)
153 @ApiController('/rgw/user', Scope
.RGW
)
154 class RgwUser(RgwRESTController
):
156 def _append_uid(self
, user
):
158 Append the user identifier that looks like [<tenant>$]<user>.
159 See http://docs.ceph.com/docs/jewel/radosgw/multitenancy/ for
161 :param user: The user parameters.
163 :return: The modified user parameters including the 'uid' parameter.
166 if isinstance(user
, dict):
167 user
['uid'] = '{}${}'.format(user
['tenant'], user
['user_id']) \
168 if user
['tenant'] else user
['user_id']
177 params
['marker'] = marker
178 result
= self
.proxy('GET', 'user?list', params
)
179 users
.extend(result
['keys'])
180 if not result
['truncated']:
182 # Make sure there is a marker.
183 assert result
['marker']
184 # Make sure the marker has changed.
185 assert marker
!= result
['marker']
186 marker
= result
['marker']
190 result
= self
.proxy('GET', 'user', {'uid': uid
})
191 return self
._append
_uid
(result
)
195 def get_emails(self
):
197 for uid
in json
.loads(self
.list()):
198 user
= json
.loads(self
.get(uid
))
200 emails
.append(user
["email"])
203 def create(self
, uid
, display_name
, email
=None, max_buckets
=None,
204 suspended
=None, generate_key
=None, access_key
=None,
206 params
= {'uid': uid
}
207 if display_name
is not None:
208 params
['display-name'] = display_name
209 if email
is not None:
210 params
['email'] = email
211 if max_buckets
is not None:
212 params
['max-buckets'] = max_buckets
213 if suspended
is not None:
214 params
['suspended'] = suspended
215 if generate_key
is not None:
216 params
['generate-key'] = generate_key
217 if access_key
is not None:
218 params
['access-key'] = access_key
219 if secret_key
is not None:
220 params
['secret-key'] = secret_key
221 result
= self
.proxy('PUT', 'user', params
)
222 return self
._append
_uid
(result
)
224 def set(self
, uid
, display_name
=None, email
=None, max_buckets
=None,
226 params
= {'uid': uid
}
227 if display_name
is not None:
228 params
['display-name'] = display_name
229 if email
is not None:
230 params
['email'] = email
231 if max_buckets
is not None:
232 params
['max-buckets'] = max_buckets
233 if suspended
is not None:
234 params
['suspended'] = suspended
235 result
= self
.proxy('POST', 'user', params
)
236 return self
._append
_uid
(result
)
238 def delete(self
, uid
):
240 instance
= RgwClient
.admin_instance()
241 # Ensure the user is not configured to access the RGW Object Gateway.
242 if instance
.userid
== uid
:
243 raise DashboardException(msg
='Unable to delete "{}" - this user '
244 'account is required for managing the '
245 'Object Gateway'.format(uid
))
246 # Finally redirect request to the RGW proxy.
247 return self
.proxy('DELETE', 'user', {'uid': uid
}, json_response
=False)
248 except (DashboardException
, RequestException
) as e
:
249 raise DashboardException(e
, component
='rgw')
251 # pylint: disable=redefined-builtin
252 @RESTController.Resource(method
='POST', path
='/capability', status
=201)
253 def create_cap(self
, uid
, type, perm
):
254 return self
.proxy('PUT', 'user?caps', {
256 'user-caps': '{}={}'.format(type, perm
)
259 # pylint: disable=redefined-builtin
260 @RESTController.Resource(method
='DELETE', path
='/capability', status
=204)
261 def delete_cap(self
, uid
, type, perm
):
262 return self
.proxy('DELETE', 'user?caps', {
264 'user-caps': '{}={}'.format(type, perm
)
267 @RESTController.Resource(method
='POST', path
='/key', status
=201)
268 def create_key(self
, uid
, key_type
='s3', subuser
=None, generate_key
='true',
269 access_key
=None, secret_key
=None):
270 params
= {'uid': uid
, 'key-type': key_type
, 'generate-key': generate_key
}
271 if subuser
is not None:
272 params
['subuser'] = subuser
273 if access_key
is not None:
274 params
['access-key'] = access_key
275 if secret_key
is not None:
276 params
['secret-key'] = secret_key
277 return self
.proxy('PUT', 'user?key', params
)
279 @RESTController.Resource(method
='DELETE', path
='/key', status
=204)
280 def delete_key(self
, uid
, key_type
='s3', subuser
=None, access_key
=None):
281 params
= {'uid': uid
, 'key-type': key_type
}
282 if subuser
is not None:
283 params
['subuser'] = subuser
284 if access_key
is not None:
285 params
['access-key'] = access_key
286 return self
.proxy('DELETE', 'user?key', params
, json_response
=False)
288 @RESTController.Resource(method
='GET', path
='/quota')
289 def get_quota(self
, uid
):
290 return self
.proxy('GET', 'user?quota', {'uid': uid
})
292 @RESTController.Resource(method
='PUT', path
='/quota')
293 def set_quota(self
, uid
, quota_type
, enabled
, max_size_kb
, max_objects
):
294 return self
.proxy('PUT', 'user?quota', {
296 'quota-type': quota_type
,
298 'max-size-kb': max_size_kb
,
299 'max-objects': max_objects
300 }, json_response
=False)
302 @RESTController.Resource(method
='POST', path
='/subuser', status
=201)
303 def create_subuser(self
, uid
, subuser
, access
, key_type
='s3',
304 generate_secret
='true', access_key
=None,
306 return self
.proxy('PUT', 'user', {
309 'key-type': key_type
,
311 'generate-secret': generate_secret
,
312 'access-key': access_key
,
313 'secret-key': secret_key
316 @RESTController.Resource(method
='DELETE', path
='/subuser/{subuser}', status
=204)
317 def delete_subuser(self
, uid
, subuser
, purge_keys
='true'):
319 :param purge_keys: Set to False to do not purge the keys.
320 Note, this only works for s3 subusers.
322 return self
.proxy('DELETE', 'user', {
325 'purge-keys': purge_keys
326 }, json_response
=False)