1 |
|
# Copyright ETSI Contributors and Others. |
2 |
|
# All Rights Reserved. |
3 |
|
# |
4 |
|
# Licensed under the Apache License, Version 2.0 (the "License"); you may |
5 |
|
# not use this file except in compliance with the License. You may obtain |
6 |
|
# a copy of the License at |
7 |
|
# |
8 |
|
# http://www.apache.org/licenses/LICENSE-2.0 |
9 |
|
# |
10 |
|
# Unless required by applicable law or agreed to in writing, software |
11 |
|
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT |
12 |
|
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the |
13 |
|
# License for the specific language governing permissions and limitations |
14 |
|
# under the License. |
15 |
|
|
16 |
1 |
import click |
17 |
1 |
from osmclient.common.exceptions import ClientException |
18 |
1 |
from osmclient.cli_commands import utils |
19 |
1 |
from prettytable import PrettyTable |
20 |
1 |
import json |
21 |
1 |
import logging |
22 |
|
|
23 |
1 |
logger = logging.getLogger("osmclient") |
24 |
|
|
25 |
|
|
26 |
|
############################## |
27 |
|
# Role Management Operations # |
28 |
|
############################## |
29 |
|
|
30 |
|
|
31 |
1 |
@click.command(name="role-create", short_help="creates a new role") |
32 |
1 |
@click.argument("name") |
33 |
1 |
@click.option("--permissions", default=None, help="role permissions using a dictionary") |
34 |
1 |
@click.pass_context |
35 |
1 |
def role_create(ctx, name, permissions): |
36 |
|
""" |
37 |
|
Creates a new role. |
38 |
|
|
39 |
|
\b |
40 |
|
NAME: Name or ID of the role. |
41 |
|
DEFINITION: Definition of grant/denial of access to resources. |
42 |
|
""" |
43 |
0 |
logger.debug("") |
44 |
0 |
utils.check_client_version(ctx.obj, ctx.command.name) |
45 |
0 |
ctx.obj.role.create(name, permissions) |
46 |
|
|
47 |
|
|
48 |
1 |
@click.command(name="role-update", short_help="updates a role") |
49 |
1 |
@click.argument("name") |
50 |
1 |
@click.option("--set-name", default=None, help="change name of rle") |
51 |
1 |
@click.option( |
52 |
|
"--add", |
53 |
|
default=None, |
54 |
|
help="yaml format dictionary with permission: True/False to access grant/denial", |
55 |
|
) |
56 |
1 |
@click.option("--remove", default=None, help="yaml format list to remove a permission") |
57 |
1 |
@click.pass_context |
58 |
1 |
def role_update(ctx, name, set_name, add, remove): |
59 |
|
""" |
60 |
|
Updates a role. |
61 |
|
|
62 |
|
\b |
63 |
|
NAME: Name or ID of the role. |
64 |
|
DEFINITION: Definition overwrites the old definition. |
65 |
|
ADD: Grant/denial of access to resource to add. |
66 |
|
REMOVE: Grant/denial of access to resource to remove. |
67 |
|
""" |
68 |
0 |
logger.debug("") |
69 |
0 |
utils.check_client_version(ctx.obj, ctx.command.name) |
70 |
0 |
ctx.obj.role.update(name, set_name, None, add, remove) |
71 |
|
|
72 |
|
|
73 |
1 |
@click.command(name="role-delete", short_help="deletes a role") |
74 |
1 |
@click.argument("name") |
75 |
1 |
@click.pass_context |
76 |
1 |
def role_delete(ctx, name): |
77 |
|
""" |
78 |
|
Deletes a role. |
79 |
|
|
80 |
|
\b |
81 |
|
NAME: Name or ID of the role. |
82 |
|
""" |
83 |
0 |
logger.debug("") |
84 |
0 |
utils.check_client_version(ctx.obj, ctx.command.name) |
85 |
0 |
ctx.obj.role.delete(name) |
86 |
|
|
87 |
|
|
88 |
1 |
@click.command(name="role-list", short_help="list all roles") |
89 |
1 |
@click.option( |
90 |
|
"--filter", |
91 |
|
default=None, |
92 |
|
multiple=True, |
93 |
|
help="restricts the list to the projects matching the filter", |
94 |
|
) |
95 |
1 |
@click.pass_context |
96 |
1 |
def role_list(ctx, filter): |
97 |
|
""" |
98 |
|
List all roles. |
99 |
|
""" |
100 |
0 |
logger.debug("") |
101 |
0 |
utils.check_client_version(ctx.obj, ctx.command.name) |
102 |
0 |
if filter: |
103 |
0 |
filter = "&".join(filter) |
104 |
0 |
resp = ctx.obj.role.list(filter) |
105 |
0 |
table = PrettyTable(["name", "id"]) |
106 |
0 |
for role in resp: |
107 |
0 |
table.add_row([role["name"], role["_id"]]) |
108 |
0 |
table.align = "l" |
109 |
0 |
print(table) |
110 |
|
|
111 |
|
|
112 |
1 |
@click.command(name="role-show", short_help="show specific role") |
113 |
1 |
@click.argument("name") |
114 |
1 |
@click.pass_context |
115 |
1 |
def role_show(ctx, name): |
116 |
|
""" |
117 |
|
Shows the details of a role. |
118 |
|
|
119 |
|
\b |
120 |
|
NAME: Name or ID of the role. |
121 |
|
""" |
122 |
0 |
logger.debug("") |
123 |
0 |
utils.check_client_version(ctx.obj, ctx.command.name) |
124 |
0 |
resp = ctx.obj.role.get(name) |
125 |
|
|
126 |
0 |
table = PrettyTable(["key", "attribute"]) |
127 |
0 |
for k, v in resp.items(): |
128 |
0 |
table.add_row([k, json.dumps(v, indent=2)]) |
129 |
0 |
table.align = "l" |
130 |
0 |
print(table) |
131 |
|
|
132 |
|
|
133 |
|
#################### |
134 |
|
# Project mgmt operations |
135 |
|
#################### |
136 |
|
|
137 |
|
|
138 |
1 |
@click.command(name="project-create", short_help="creates a new project") |
139 |
1 |
@click.argument("name") |
140 |
|
# @click.option('--description', |
141 |
|
# default='no description', |
142 |
|
# help='human readable description') |
143 |
1 |
@click.option("--domain-name", "domain_name", default=None, help="assign to a domain") |
144 |
1 |
@click.option( |
145 |
|
"--quotas", |
146 |
|
"quotas", |
147 |
|
multiple=True, |
148 |
|
default=None, |
149 |
|
help="provide quotas. Can be used several times: 'quota1=number[,quota2=number,...]'. Quotas can be one " |
150 |
|
"of vnfds, nsds, nsts, pdus, nsrs, nsis, vim_accounts, wim_accounts, sdns, k8sclusters, k8srepos", |
151 |
|
) |
152 |
1 |
@click.pass_context |
153 |
1 |
def project_create(ctx, name, domain_name, quotas): |
154 |
|
"""Creates a new project |
155 |
|
|
156 |
|
NAME: name of the project |
157 |
|
DOMAIN_NAME: optional domain name for the project when keystone authentication is used |
158 |
|
QUOTAS: set quotas for the project |
159 |
|
""" |
160 |
0 |
logger.debug("") |
161 |
0 |
project = {"name": name} |
162 |
0 |
if domain_name: |
163 |
0 |
project["domain_name"] = domain_name |
164 |
0 |
quotas_dict = _process_project_quotas(quotas) |
165 |
0 |
if quotas_dict: |
166 |
0 |
project["quotas"] = quotas_dict |
167 |
|
|
168 |
0 |
utils.check_client_version(ctx.obj, ctx.command.name) |
169 |
0 |
ctx.obj.project.create(name, project) |
170 |
|
|
171 |
|
|
172 |
1 |
def _process_project_quotas(quota_list): |
173 |
0 |
quotas_dict = {} |
174 |
0 |
if not quota_list: |
175 |
0 |
return quotas_dict |
176 |
0 |
try: |
177 |
0 |
for quota in quota_list: |
178 |
0 |
for single_quota in quota.split(","): |
179 |
0 |
k, v = single_quota.split("=") |
180 |
0 |
quotas_dict[k] = None if v in ("None", "null", "") else int(v) |
181 |
0 |
except (ValueError, TypeError): |
182 |
0 |
raise ClientException( |
183 |
|
"invalid format for 'quotas'. Use 'k1=v1,v1=v2'. v must be a integer or null" |
184 |
|
) |
185 |
0 |
return quotas_dict |
186 |
|
|
187 |
|
|
188 |
1 |
@click.command(name="project-delete", short_help="deletes a project") |
189 |
1 |
@click.argument("name") |
190 |
1 |
@click.pass_context |
191 |
1 |
def project_delete(ctx, name): |
192 |
|
"""deletes a project |
193 |
|
|
194 |
|
NAME: name or ID of the project to be deleted |
195 |
|
""" |
196 |
0 |
logger.debug("") |
197 |
0 |
utils.check_client_version(ctx.obj, ctx.command.name) |
198 |
0 |
ctx.obj.project.delete(name) |
199 |
|
|
200 |
|
|
201 |
1 |
@click.command(name="project-list", short_help="list all projects") |
202 |
1 |
@click.option( |
203 |
|
"--filter", |
204 |
|
default=None, |
205 |
|
multiple=True, |
206 |
|
help="restricts the list to the projects matching the filter", |
207 |
|
) |
208 |
1 |
@click.pass_context |
209 |
1 |
def project_list(ctx, filter): |
210 |
|
"""list all projects""" |
211 |
0 |
logger.debug("") |
212 |
0 |
utils.check_client_version(ctx.obj, ctx.command.name) |
213 |
0 |
if filter: |
214 |
0 |
filter = "&".join(filter) |
215 |
0 |
resp = ctx.obj.project.list(filter) |
216 |
0 |
table = PrettyTable(["name", "id"]) |
217 |
0 |
for proj in resp: |
218 |
0 |
table.add_row([proj["name"], proj["_id"]]) |
219 |
0 |
table.align = "l" |
220 |
0 |
print(table) |
221 |
|
|
222 |
|
|
223 |
1 |
@click.command(name="project-show", short_help="shows the details of a project") |
224 |
1 |
@click.argument("name") |
225 |
1 |
@click.pass_context |
226 |
1 |
def project_show(ctx, name): |
227 |
|
"""shows the details of a project |
228 |
|
|
229 |
|
NAME: name or ID of the project |
230 |
|
""" |
231 |
0 |
logger.debug("") |
232 |
0 |
utils.check_client_version(ctx.obj, ctx.command.name) |
233 |
0 |
resp = ctx.obj.project.get(name) |
234 |
|
|
235 |
0 |
table = PrettyTable(["key", "attribute"]) |
236 |
0 |
for k, v in resp.items(): |
237 |
0 |
table.add_row([k, json.dumps(v, indent=2)]) |
238 |
0 |
table.align = "l" |
239 |
0 |
print(table) |
240 |
|
|
241 |
|
|
242 |
1 |
@click.command( |
243 |
|
name="project-update", short_help="updates a project (only the name can be updated)" |
244 |
|
) |
245 |
1 |
@click.argument("project") |
246 |
1 |
@click.option("--name", default=None, help="new name for the project") |
247 |
1 |
@click.option( |
248 |
|
"--quotas", |
249 |
|
"quotas", |
250 |
|
multiple=True, |
251 |
|
default=None, |
252 |
|
help="change quotas. Can be used several times: 'quota1=number|empty[,quota2=...]' " |
253 |
|
"(use empty to reset quota to default", |
254 |
|
) |
255 |
1 |
@click.pass_context |
256 |
1 |
def project_update(ctx, project, name, quotas): |
257 |
|
""" |
258 |
|
Update a project name |
259 |
|
|
260 |
|
:param ctx: |
261 |
|
:param project: id or name of the project to modify |
262 |
|
:param name: new name for the project |
263 |
|
:param quotas: change quotas of the project |
264 |
|
:return: |
265 |
|
""" |
266 |
0 |
logger.debug("") |
267 |
0 |
project_changes = {} |
268 |
0 |
if name: |
269 |
0 |
project_changes["name"] = name |
270 |
0 |
quotas_dict = _process_project_quotas(quotas) |
271 |
0 |
if quotas_dict: |
272 |
0 |
project_changes["quotas"] = quotas_dict |
273 |
|
|
274 |
0 |
utils.check_client_version(ctx.obj, ctx.command.name) |
275 |
0 |
ctx.obj.project.update(project, project_changes) |
276 |
|
|
277 |
|
|
278 |
|
#################### |
279 |
|
# User mgmt operations |
280 |
|
#################### |
281 |
|
|
282 |
|
|
283 |
1 |
@click.command(name="user-create", short_help="creates a new user") |
284 |
1 |
@click.argument("username") |
285 |
1 |
@click.option( |
286 |
|
"--password", |
287 |
|
prompt=True, |
288 |
|
hide_input=True, |
289 |
|
confirmation_prompt=True, |
290 |
|
help="user password", |
291 |
|
) |
292 |
1 |
@click.option( |
293 |
|
"--projects", |
294 |
|
# prompt="Comma separate list of projects", |
295 |
|
multiple=True, |
296 |
|
callback=lambda ctx, param, value: "".join(value).split(",") |
297 |
|
if all(len(x) == 1 for x in value) |
298 |
|
else value, |
299 |
|
help="list of project ids that the user belongs to", |
300 |
|
) |
301 |
1 |
@click.option( |
302 |
|
"--project-role-mappings", |
303 |
|
"project_role_mappings", |
304 |
|
default=None, |
305 |
|
multiple=True, |
306 |
|
help="assign role(s) in a project. Can be used several times: 'project,role1[,role2,...]'", |
307 |
|
) |
308 |
1 |
@click.option("--domain-name", "domain_name", default=None, help="assign to a domain") |
309 |
1 |
@click.pass_context |
310 |
1 |
def user_create(ctx, username, password, projects, project_role_mappings, domain_name): |
311 |
|
"""Creates a new user |
312 |
|
|
313 |
|
\b |
314 |
|
USERNAME: name of the user |
315 |
|
PASSWORD: password of the user |
316 |
|
PROJECTS: projects assigned to user (internal only) |
317 |
|
PROJECT_ROLE_MAPPING: roles in projects assigned to user (keystone) |
318 |
|
DOMAIN_NAME: optional domain name for the user when keystone authentication is used |
319 |
|
""" |
320 |
0 |
logger.debug("") |
321 |
0 |
user = {} |
322 |
0 |
user["username"] = username |
323 |
0 |
user["password"] = password |
324 |
0 |
user["projects"] = projects |
325 |
0 |
user["project_role_mappings"] = project_role_mappings |
326 |
0 |
if domain_name: |
327 |
0 |
user["domain_name"] = domain_name |
328 |
|
|
329 |
0 |
utils.check_client_version(ctx.obj, ctx.command.name) |
330 |
0 |
ctx.obj.user.create(username, user) |
331 |
|
|
332 |
|
|
333 |
1 |
@click.command(name="user-update", short_help="updates user information") |
334 |
1 |
@click.argument("username") |
335 |
1 |
@click.option( |
336 |
|
"--password", |
337 |
|
# prompt=True, |
338 |
|
# hide_input=True, |
339 |
|
# confirmation_prompt=True, |
340 |
|
help="user password", |
341 |
|
) |
342 |
1 |
@click.option("--set-username", "set_username", default=None, help="change username") |
343 |
1 |
@click.option( |
344 |
|
"--set-project", |
345 |
|
"set_project", |
346 |
|
default=None, |
347 |
|
multiple=True, |
348 |
|
help="create/replace the roles for this project: 'project,role1[,role2,...]'", |
349 |
|
) |
350 |
1 |
@click.option( |
351 |
|
"--remove-project", |
352 |
|
"remove_project", |
353 |
|
default=None, |
354 |
|
multiple=True, |
355 |
|
help="removes project from user: 'project'", |
356 |
|
) |
357 |
1 |
@click.option( |
358 |
|
"--add-project-role", |
359 |
|
"add_project_role", |
360 |
|
default=None, |
361 |
|
multiple=True, |
362 |
|
help="assign role(s) in a project. Can be used several times: 'project,role1[,role2,...]'", |
363 |
|
) |
364 |
1 |
@click.option( |
365 |
|
"--remove-project-role", |
366 |
|
"remove_project_role", |
367 |
|
default=None, |
368 |
|
multiple=True, |
369 |
|
help="remove role(s) in a project. Can be used several times: 'project,role1[,role2,...]'", |
370 |
|
) |
371 |
1 |
@click.option("--change_password", "change_password", help="user's current password") |
372 |
1 |
@click.option( |
373 |
|
"--new_password", |
374 |
|
"new_password", |
375 |
|
help="user's new password to update in expiry condition", |
376 |
|
) |
377 |
1 |
@click.pass_context |
378 |
1 |
def user_update( |
379 |
|
ctx, |
380 |
|
username, |
381 |
|
password, |
382 |
|
set_username, |
383 |
|
set_project, |
384 |
|
remove_project, |
385 |
|
add_project_role, |
386 |
|
remove_project_role, |
387 |
|
change_password, |
388 |
|
new_password, |
389 |
|
): |
390 |
|
"""Update a user information |
391 |
|
|
392 |
|
\b |
393 |
|
USERNAME: name of the user |
394 |
|
PASSWORD: new password |
395 |
|
SET_USERNAME: new username |
396 |
|
SET_PROJECT: creating mappings for project/role(s) |
397 |
|
REMOVE_PROJECT: deleting mappings for project/role(s) |
398 |
|
ADD_PROJECT_ROLE: adding mappings for project/role(s) |
399 |
|
REMOVE_PROJECT_ROLE: removing mappings for project/role(s) |
400 |
|
CHANGE_PASSWORD: user's current password to change |
401 |
|
NEW_PASSWORD: user's new password to update in expiry condition |
402 |
|
""" |
403 |
0 |
logger.debug("") |
404 |
0 |
user = {} |
405 |
0 |
user["password"] = password |
406 |
0 |
user["username"] = set_username |
407 |
0 |
user["set-project"] = set_project |
408 |
0 |
user["remove-project"] = remove_project |
409 |
0 |
user["add-project-role"] = add_project_role |
410 |
0 |
user["remove-project-role"] = remove_project_role |
411 |
0 |
user["change_password"] = change_password |
412 |
0 |
user["new_password"] = new_password |
413 |
|
|
414 |
0 |
utils.check_client_version(ctx.obj, ctx.command.name) |
415 |
0 |
ctx.obj.user.update(username, user) |
416 |
0 |
if not user.get("change_password"): |
417 |
0 |
ctx.obj.user.update(username, user) |
418 |
|
else: |
419 |
0 |
ctx.obj.user.update(username, user, pwd_change=True) |
420 |
|
|
421 |
|
|
422 |
1 |
@click.command(name="user-delete", short_help="deletes a user") |
423 |
1 |
@click.argument("name") |
424 |
|
# @click.option('--force', is_flag=True, help='forces the deletion bypassing pre-conditions') |
425 |
1 |
@click.pass_context |
426 |
1 |
def user_delete(ctx, name): |
427 |
|
"""deletes a user |
428 |
|
|
429 |
|
\b |
430 |
|
NAME: name or ID of the user to be deleted |
431 |
|
""" |
432 |
0 |
logger.debug("") |
433 |
0 |
utils.check_client_version(ctx.obj, ctx.command.name) |
434 |
0 |
ctx.obj.user.delete(name) |
435 |
|
|
436 |
|
|
437 |
1 |
@click.command(name="user-list", short_help="list all users") |
438 |
1 |
@click.option( |
439 |
|
"--filter", |
440 |
|
default=None, |
441 |
|
multiple=True, |
442 |
|
help="restricts the list to the users matching the filter", |
443 |
|
) |
444 |
1 |
@click.pass_context |
445 |
1 |
def user_list(ctx, filter): |
446 |
|
"""list all users""" |
447 |
0 |
utils.check_client_version(ctx.obj, ctx.command.name) |
448 |
0 |
if filter: |
449 |
0 |
filter = "&".join(filter) |
450 |
0 |
resp = ctx.obj.user.list(filter) |
451 |
0 |
table = PrettyTable(["name", "id"]) |
452 |
0 |
for user in resp: |
453 |
0 |
table.add_row([user["username"], user["_id"]]) |
454 |
0 |
table.align = "l" |
455 |
0 |
print(table) |
456 |
|
|
457 |
|
|
458 |
1 |
@click.command(name="user-show", short_help="shows the details of a user") |
459 |
1 |
@click.argument("name") |
460 |
1 |
@click.pass_context |
461 |
1 |
def user_show(ctx, name): |
462 |
|
"""shows the details of a user |
463 |
|
|
464 |
|
NAME: name or ID of the user |
465 |
|
""" |
466 |
0 |
logger.debug("") |
467 |
0 |
utils.check_client_version(ctx.obj, ctx.command.name) |
468 |
0 |
resp = ctx.obj.user.get(name) |
469 |
0 |
if "password" in resp: |
470 |
0 |
resp["password"] = "********" |
471 |
|
|
472 |
0 |
table = PrettyTable(["key", "attribute"]) |
473 |
0 |
for k, v in resp.items(): |
474 |
0 |
table.add_row([k, json.dumps(v, indent=2)]) |
475 |
0 |
table.align = "l" |
476 |
0 |
print(table) |