blob: 71fa12bec8712c62f06fd6f39c515491d6aec8d3 [file] [log] [blame]
# Copyright ETSI Contributors and Others.
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import click
from osmclient.common.exceptions import ClientException
from osmclient.cli_commands import utils
from prettytable import PrettyTable
import json
import logging
import time
logger = logging.getLogger("osmclient")
##############################
# Role Management Operations #
##############################
@click.command(name="role-create", short_help="creates a new role")
@click.argument("name")
@click.option("--permissions", default=None, help="role permissions using a dictionary")
@click.pass_context
def role_create(ctx, name, permissions):
"""
Creates a new role.
\b
NAME: Name or ID of the role.
DEFINITION: Definition of grant/denial of access to resources.
"""
logger.debug("")
utils.check_client_version(ctx.obj, ctx.command.name)
ctx.obj.role.create(name, permissions)
@click.command(name="role-update", short_help="updates a role")
@click.argument("name")
@click.option("--set-name", default=None, help="change name of rle")
@click.option(
"--add",
default=None,
help="yaml format dictionary with permission: True/False to access grant/denial",
)
@click.option("--remove", default=None, help="yaml format list to remove a permission")
@click.pass_context
def role_update(ctx, name, set_name, add, remove):
"""
Updates a role.
\b
NAME: Name or ID of the role.
DEFINITION: Definition overwrites the old definition.
ADD: Grant/denial of access to resource to add.
REMOVE: Grant/denial of access to resource to remove.
"""
logger.debug("")
utils.check_client_version(ctx.obj, ctx.command.name)
ctx.obj.role.update(name, set_name, None, add, remove)
@click.command(name="role-delete", short_help="deletes a role")
@click.argument("name")
@click.pass_context
def role_delete(ctx, name):
"""
Deletes a role.
\b
NAME: Name or ID of the role.
"""
logger.debug("")
utils.check_client_version(ctx.obj, ctx.command.name)
ctx.obj.role.delete(name)
@click.command(name="role-list", short_help="list all roles")
@click.option(
"--filter",
default=None,
multiple=True,
help="restricts the list to the projects matching the filter",
)
@click.pass_context
def role_list(ctx, filter):
"""
List all roles.
"""
logger.debug("")
utils.check_client_version(ctx.obj, ctx.command.name)
if filter:
filter = "&".join(filter)
resp = ctx.obj.role.list(filter)
table = PrettyTable(["name", "id"])
for role in resp:
table.add_row([role["name"], role["_id"]])
table.align = "l"
print(table)
@click.command(name="role-show", short_help="show specific role")
@click.argument("name")
@click.pass_context
def role_show(ctx, name):
"""
Shows the details of a role.
\b
NAME: Name or ID of the role.
"""
logger.debug("")
utils.check_client_version(ctx.obj, ctx.command.name)
resp = ctx.obj.role.get(name)
table = PrettyTable(["key", "attribute"])
for k, v in resp.items():
table.add_row([k, json.dumps(v, indent=2)])
table.align = "l"
print(table)
####################
# Project mgmt operations
####################
@click.command(name="project-create", short_help="creates a new project")
@click.argument("name")
# @click.option('--description',
# default='no description',
# help='human readable description')
@click.option("--domain-name", "domain_name", default=None, help="assign to a domain")
@click.option(
"--quotas",
"quotas",
multiple=True,
default=None,
help="provide quotas. Can be used several times: 'quota1=number[,quota2=number,...]'. Quotas can be one "
"of vnfds, nsds, nsts, pdus, nsrs, nsis, vim_accounts, wim_accounts, sdns, k8sclusters, k8srepos",
)
@click.pass_context
def project_create(ctx, name, domain_name, quotas):
"""Creates a new project
NAME: name of the project
DOMAIN_NAME: optional domain name for the project when keystone authentication is used
QUOTAS: set quotas for the project
"""
logger.debug("")
project = {"name": name}
if domain_name:
project["domain_name"] = domain_name
quotas_dict = _process_project_quotas(quotas)
if quotas_dict:
project["quotas"] = quotas_dict
utils.check_client_version(ctx.obj, ctx.command.name)
ctx.obj.project.create(name, project)
def _process_project_quotas(quota_list):
quotas_dict = {}
if not quota_list:
return quotas_dict
try:
for quota in quota_list:
for single_quota in quota.split(","):
k, v = single_quota.split("=")
quotas_dict[k] = None if v in ("None", "null", "") else int(v)
except (ValueError, TypeError):
raise ClientException(
"invalid format for 'quotas'. Use 'k1=v1,v1=v2'. v must be a integer or null"
)
return quotas_dict
@click.command(name="project-delete", short_help="deletes a project")
@click.argument("name")
@click.pass_context
def project_delete(ctx, name):
"""deletes a project
NAME: name or ID of the project to be deleted
"""
logger.debug("")
utils.check_client_version(ctx.obj, ctx.command.name)
ctx.obj.project.delete(name)
@click.command(name="project-list", short_help="list all projects")
@click.option(
"--filter",
default=None,
multiple=True,
help="restricts the list to the projects matching the filter",
)
@click.pass_context
def project_list(ctx, filter):
"""list all projects"""
logger.debug("")
utils.check_client_version(ctx.obj, ctx.command.name)
if filter:
filter = "&".join(filter)
resp = ctx.obj.project.list(filter)
table = PrettyTable(["name", "id"])
for proj in resp:
table.add_row([proj["name"], proj["_id"]])
table.align = "l"
print(table)
@click.command(name="project-show", short_help="shows the details of a project")
@click.argument("name")
@click.pass_context
def project_show(ctx, name):
"""shows the details of a project
NAME: name or ID of the project
"""
logger.debug("")
utils.check_client_version(ctx.obj, ctx.command.name)
resp = ctx.obj.project.get(name)
table = PrettyTable(["key", "attribute"])
for k, v in resp.items():
table.add_row([k, json.dumps(v, indent=2)])
table.align = "l"
print(table)
@click.command(
name="project-update", short_help="updates a project (only the name can be updated)"
)
@click.argument("project")
@click.option("--name", default=None, help="new name for the project")
@click.option(
"--quotas",
"quotas",
multiple=True,
default=None,
help="change quotas. Can be used several times: 'quota1=number|empty[,quota2=...]' "
"(use empty to reset quota to default",
)
@click.pass_context
def project_update(ctx, project, name, quotas):
"""
Update a project name
:param ctx:
:param project: id or name of the project to modify
:param name: new name for the project
:param quotas: change quotas of the project
:return:
"""
logger.debug("")
project_changes = {}
if name:
project_changes["name"] = name
quotas_dict = _process_project_quotas(quotas)
if quotas_dict:
project_changes["quotas"] = quotas_dict
utils.check_client_version(ctx.obj, ctx.command.name)
ctx.obj.project.update(project, project_changes)
####################
# User mgmt operations
####################
@click.command(name="user-create", short_help="creates a new user")
@click.argument("username")
@click.option(
"--password",
prompt=True,
hide_input=True,
confirmation_prompt=True,
help="user password",
)
@click.option(
"--projects",
# prompt="Comma separate list of projects",
multiple=True,
callback=lambda ctx, param, value: (
"".join(value).split(",") if all(len(x) == 1 for x in value) else value
),
help="list of project ids that the user belongs to",
)
@click.option(
"--project-role-mappings",
"project_role_mappings",
default=None,
multiple=True,
help="assign role(s) in a project. Can be used several times: 'project,role1[,role2,...]'",
)
@click.option("--domain-name", "domain_name", default=None, help="assign to a domain")
@click.pass_context
def user_create(ctx, username, password, projects, project_role_mappings, domain_name):
"""Creates a new user
\b
USERNAME: name of the user
PASSWORD: password of the user
PROJECTS: projects assigned to user (internal only)
PROJECT_ROLE_MAPPING: roles in projects assigned to user (keystone)
DOMAIN_NAME: optional domain name for the user when keystone authentication is used
"""
logger.debug("")
user = {}
user["username"] = username
user["password"] = password
user["projects"] = projects
user["project_role_mappings"] = project_role_mappings
if domain_name:
user["domain_name"] = domain_name
utils.check_client_version(ctx.obj, ctx.command.name)
ctx.obj.user.create(username, user)
@click.command(name="user-update", short_help="updates user information")
@click.argument("username")
@click.option("--set-username", "set_username", default=None, help="change username")
@click.option(
"--set-project",
"set_project",
default=None,
multiple=True,
help="create/replace the roles for this project: 'project,role1[,role2,...]'",
)
@click.option(
"--remove-project",
"remove_project",
default=None,
multiple=True,
help="removes project from user: 'project'",
)
@click.option(
"--add-project-role",
"add_project_role",
default=None,
multiple=True,
help="assign role(s) in a project. Can be used several times: 'project,role1[,role2,...]'",
)
@click.option(
"--remove-project-role",
"remove_project_role",
default=None,
multiple=True,
help="remove role(s) in a project. Can be used several times: 'project,role1[,role2,...]'",
)
@click.option("--current-password", "current_password", help="user's current password")
@click.option(
"--new-password",
"new_password",
# prompt=True,
# hide_input=True,
# confirmation_prompt=True,
help="new user password",
)
@click.option(
"--unlock",
is_flag=True,
help="unlock user",
)
@click.option(
"--renew",
is_flag=True,
help="renew user",
)
@click.pass_context
def user_update(
ctx,
username,
set_username,
set_project,
remove_project,
add_project_role,
remove_project_role,
current_password,
new_password,
unlock,
renew,
):
"""Update a user information
\b
USERNAME: name of the user
SET_USERNAME: new username
SET_PROJECT: creating mappings for project/role(s)
REMOVE_PROJECT: deleting mappings for project/role(s)
ADD_PROJECT_ROLE: adding mappings for project/role(s)
REMOVE_PROJECT_ROLE: removing mappings for project/role(s)
CURRENT_PASSWORD: user's current password to change
NEW_PASSWORD: user's new password to be updated
UNLOCK: unlock user
RENEW: renew user
"""
logger.debug("")
user = {}
user["username"] = set_username
user["set-project"] = set_project
user["remove-project"] = remove_project
user["add-project-role"] = add_project_role
user["remove-project-role"] = remove_project_role
user["current_password"] = current_password
user["new_password"] = new_password
user["unlock"] = unlock
user["renew"] = renew
utils.check_client_version(ctx.obj, ctx.command.name)
if not user.get("current_password"):
# In case the password is valid but the end user wants to update it
ctx.obj.user.update(username, user)
else:
# In case the password has expired (also applies in first login)
ctx.obj.user.update(username, user, pwd_change=True)
@click.command(name="user-delete", short_help="deletes a user")
@click.argument("name")
# @click.option('--force', is_flag=True, help='forces the deletion bypassing pre-conditions')
@click.pass_context
def user_delete(ctx, name):
"""deletes a user
\b
NAME: name or ID of the user to be deleted
"""
logger.debug("")
utils.check_client_version(ctx.obj, ctx.command.name)
ctx.obj.user.delete(name)
@click.command(name="user-list", short_help="list all users")
@click.option(
"--filter",
default=None,
multiple=True,
help="restricts the list to the users matching the filter",
)
@click.pass_context
def user_list(ctx, filter):
"""list all users"""
utils.check_client_version(ctx.obj, ctx.command.name)
if filter:
filter = "&".join(filter)
resp, admin_show = ctx.obj.user.list(filter)
for user in resp:
if user["username"] == "admin":
user["_admin"]["account_expire_time"] = "N/A"
if admin_show:
table = PrettyTable(["name", "id", "user_status", "expires_in"])
for user in resp:
table.add_row(
[
user["username"],
user["_id"],
user["_admin"]["user_status"].upper(),
(
time.strftime(
"%b-%d-%Y %X",
time.gmtime(user["_admin"]["account_expire_time"]),
)
if not user["username"] == "admin"
else user["_admin"]["account_expire_time"]
),
]
)
else:
table = PrettyTable(["name", "id"])
for user in resp:
table.add_row([user["username"], user["_id"]])
table.align = "l"
print(table)
@click.command(name="user-show", short_help="shows the details of a user")
@click.argument("name")
@click.pass_context
def user_show(ctx, name):
"""shows the details of a user
NAME: name or ID of the user
"""
logger.debug("")
utils.check_client_version(ctx.obj, ctx.command.name)
resp = ctx.obj.user.get(name)
if "password" in resp:
resp["password"] = "********"
table = PrettyTable(["key", "attribute"])
for k, v in resp.items():
table.add_row([k, json.dumps(v, indent=2)])
table.align = "l"
print(table)