1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
|
import logging
from errno import EINVAL
from typing import List, NamedTuple, Optional
from ..exceptions import DashboardException
from ..security import Scope
from ..services.ceph_service import CephService, SendCommandError
from . import APIDoc, APIRouter, CRUDCollectionMethod, CRUDEndpoint, \
EndpointDoc, RESTController, SecretStr
from ._crud import ArrayHorizontalContainer, CRUDMeta, Form, FormField, \
FormTaskInfo, Icon, MethodType, SelectionType, TableAction, Validator, \
VerticalContainer
logger = logging.getLogger("controllers.ceph_users")
class CephUserCaps(NamedTuple):
mon: str
osd: str
mgr: str
mds: str
class Cap(NamedTuple):
entity: str
cap: str
class CephUserEndpoints:
@staticmethod
def _run_auth_command(command: str, *args, **kwargs):
try:
return CephService.send_command('mon', command, *args, **kwargs)
except SendCommandError as ex:
msg = f'{ex} in command {ex.prefix}'
if ex.errno == -EINVAL:
raise DashboardException(msg, code=400)
raise DashboardException(msg, code=500)
@staticmethod
def user_list(_):
"""
Get list of ceph users and its respective data
"""
return CephUserEndpoints._run_auth_command('auth ls')["auth_dump"]
@staticmethod
def user_create(_, user_entity: str = '', capabilities: Optional[List[Cap]] = None,
import_data: str = ''):
"""
Add a ceph user with its defined capabilities.
:param user_entity: Entity to change
:param capabilities: List of capabilities to add to user_entity
"""
# Caps are represented as a vector in mon auth add commands.
# Look at AuthMonitor.cc::valid_caps for reference.
if import_data:
logger.debug("Sending import command 'auth import' \n%s", import_data)
CephUserEndpoints._run_auth_command('auth import', inbuf=import_data)
return "Successfully imported user"
assert user_entity
caps = []
for cap in capabilities:
caps.append(cap['entity'])
caps.append(cap['cap'])
logger.debug("Sending command 'auth add' of entity '%s' with caps '%s'",
user_entity, str(caps))
CephUserEndpoints._run_auth_command('auth add', entity=user_entity, caps=caps)
return f"Successfully created user '{user_entity}'"
@staticmethod
def user_delete(_, user_entity: str):
"""
Delete a ceph user and it's defined capabilities.
:param user_entity: Entity to delete
"""
logger.debug("Sending command 'auth del' of entity '%s'", user_entity)
CephUserEndpoints._run_auth_command('auth del', entity=user_entity)
return f"Successfully deleted user '{user_entity}'"
@staticmethod
def export(_, entities: List[str]):
export_string = ""
for entity in entities:
out = CephUserEndpoints._run_auth_command('auth export', entity=entity, to_json=False)
export_string += f'{out}\n'
return export_string
@staticmethod
def user_edit(_, user_entity: str = '', capabilities: List[Cap] = None):
"""
Change the ceph user capabilities.
Setting new capabilities will overwrite current ones.
:param user_entity: Entity to change
:param capabilities: List of updated capabilities to user_entity
"""
caps = []
for cap in capabilities:
caps.append(cap['entity'])
caps.append(cap['cap'])
logger.debug("Sending command 'auth caps' of entity '%s' with caps '%s'",
user_entity, str(caps))
CephUserEndpoints._run_auth_command('auth caps', entity=user_entity, caps=caps)
return f"Successfully edited user '{user_entity}'"
@staticmethod
def model(user_entity: str):
user_data = CephUserEndpoints._run_auth_command('auth get', entity=user_entity)[0]
model = {'user_entity': '', 'capabilities': []}
model['user_entity'] = user_data['entity']
for entity, cap in user_data['caps'].items():
model['capabilities'].append({'entity': entity, 'cap': cap})
return model
cap_container = ArrayHorizontalContainer('Capabilities', 'capabilities', fields=[
FormField('Entity', 'entity',
field_type=str),
FormField('Entity Capabilities',
'cap', field_type=str)
], min_items=1)
create_container = VerticalContainer('Create User', 'create_user', fields=[
FormField('User entity', 'user_entity',
field_type=str),
cap_container,
])
edit_container = VerticalContainer('Edit User', 'edit_user', fields=[
FormField('User entity', 'user_entity',
field_type=str, readonly=True),
cap_container,
])
create_form = Form(path='/cluster/user/create',
root_container=create_container,
method_type=MethodType.POST.value,
task_info=FormTaskInfo("Ceph user '{user_entity}' successfully",
['user_entity']))
# pylint: disable=C0301
import_user_help = (
'The imported file should be a keyring file and it must follow the schema described <a ' # noqa: E501
'href="https://docs.ceph.com/en/latest/rados/operations/user-management/#authorization-capabilities"' # noqa: E501
'target="_blank">here.</a>'
)
import_container = VerticalContainer('Import User', 'import_user', fields=[
FormField('User file import', 'import_data',
field_type="file", validators=[Validator.FILE],
help=import_user_help),
])
import_user_form = Form(path='/cluster/user/import',
root_container=import_container,
task_info=FormTaskInfo("successfully", []),
method_type=MethodType.POST.value)
edit_form = Form(path='/cluster/user/edit',
root_container=edit_container,
method_type=MethodType.PUT.value,
task_info=FormTaskInfo("Ceph user '{user_entity}' successfully",
['user_entity']),
model_callback=CephUserEndpoints.model)
@CRUDEndpoint(
router=APIRouter('/cluster/user', Scope.CONFIG_OPT),
doc=APIDoc("Get Ceph Users", "Cluster"),
set_column={"caps": {"cellTemplate": "badgeDict"}},
actions=[
TableAction(name='Create', permission='create', icon=Icon.ADD.value,
routerLink='/cluster/user/create'),
TableAction(name='Edit', permission='update', icon=Icon.EDIT.value,
click='edit', routerLink='/cluster/user/edit'),
TableAction(name='Delete', permission='delete', icon=Icon.DESTROY.value,
click='delete', disable=True),
TableAction(name='Import', permission='create', icon=Icon.IMPORT.value,
routerLink='/cluster/user/import'),
TableAction(name='Export', permission='read', icon=Icon.EXPORT.value,
click='authExport', disable=True)
],
permissions=[Scope.CONFIG_OPT],
forms=[create_form, edit_form, import_user_form],
column_key='entity',
resource='user',
get_all=CRUDCollectionMethod(
func=CephUserEndpoints.user_list,
doc=EndpointDoc("Get Ceph Users")
),
create=CRUDCollectionMethod(
func=CephUserEndpoints.user_create,
doc=EndpointDoc("Create Ceph User")
),
edit=CRUDCollectionMethod(
func=CephUserEndpoints.user_edit,
doc=EndpointDoc("Edit Ceph User")
),
delete=CRUDCollectionMethod(
func=CephUserEndpoints.user_delete,
doc=EndpointDoc("Delete Ceph User")
),
extra_endpoints=[
('export', CRUDCollectionMethod(
func=RESTController.Collection('POST', 'export')(CephUserEndpoints.export),
doc=EndpointDoc("Export Ceph Users")
))
],
selection_type=SelectionType.MULTI,
meta=CRUDMeta()
)
class CephUser(NamedTuple):
entity: str
caps: List[CephUserCaps]
key: SecretStr
|