Tons of clean up, some new features, some bug fixes

* new function for cleaning up expired tokens
* new function to cancel a task directly
* lots of clean up
* reworked some functions to work with more than one item

Change-Id: I21683af24410c7a602c2d5a7fb93f7cbc1f605e5
This commit is contained in:
adriant 2016-02-02 14:53:00 +13:00
parent 726a43c9dd
commit 9b84cac78f
12 changed files with 119 additions and 94 deletions

View File

@ -1,5 +1,4 @@
include AUTHORS
include babel.cfg
include LICENSE
include README.md
include ChangeLog

0
README.md Normal file
View File

View File

@ -1 +0,0 @@
[python: **.py]

View File

@ -4,14 +4,12 @@
Babel>=1.3
pbr<2.0,>=1.4
argparse
iso8601>=0.1.9
PrettyTable<0.8,>=0.7
oslo.i18n>=1.5.0 # Apache-2.0
oslo.serialization>=1.4.0 # Apache-2.0
oslo.utils>=2.0.0 # Apache-2.0
oslo.i18n>=1.5.0
oslo.serialization>=1.4.0
oslo.utils>=2.0.0
python-keystoneclient>=1.6.0
python-swiftclient>=2.2.0
PyYAML>=3.1.0
requests>=2.5.2
six>=1.9.0

View File

@ -35,9 +35,9 @@ class Client(object):
"""Initialize a new client for the Stacktask v1 API."""
self.http_client = http._construct_http_client(*args, **kwargs)
self.users = users.UsersManager(self.http_client)
self.user_roles = roles.UserRolesManager(self.http_client)
self.managed_roles = roles.ManagedRolesManager(self.http_client)
self.users = users.UserManager(self.http_client)
self.user_roles = roles.UserRoleManager(self.http_client)
self.managed_roles = roles.ManagedRoleManager(self.http_client)
self.tokens = tokens.TokenManager(self.http_client)
self.tasks = tasks.TaskManager(self.http_client)
self.notifications = notifications.NotificationManager(

View File

@ -24,7 +24,7 @@ class Notification(base.Resource):
class NotificationManager(base.ManagerWithFind):
resource_class = Notification
def show(self, note_id):
def get(self, note_id):
return self._get("/notifications/%s" % base.getid(note_id))
def list(self, filters, **kwargs):

View File

@ -18,21 +18,21 @@ from stacktaskclient.openstack.common.apiclient import base
from stacktaskclient import exc
class Roles(base.Resource):
class Role(base.Resource):
pass
class ManagableRoles(base.Resource):
class ManagableRole(base.Resource):
pass
class ManagedRolesManager(base.ManagerWithFind):
resource_class = ManagableRoles
class ManagedRoleManager(base.ManagerWithFind):
resource_class = ManagableRole
def list(self, **kwargs):
"""Get a list of roles that can be managed.
:rtype: list of :class:`Roles`
:rtype: list of :class:`Role`
"""
params = {}
url = '/openstack/roles?%(params)s' % {
@ -40,7 +40,7 @@ class ManagedRolesManager(base.ManagerWithFind):
}
return self._list(url, 'roles')
def show(self, role_id):
def get(self, role_id):
"""
Get a role by role_id
"""
@ -53,8 +53,8 @@ class ManagedRolesManager(base.ManagerWithFind):
raise exc.NotFound()
class UserRolesManager(base.BaseManager):
resource_class = Roles
class UserRoleManager(base.BaseManager):
resource_class = Role
def list(self, **kwargs):
"""List roles for a given user"""
@ -62,15 +62,20 @@ class UserRolesManager(base.BaseManager):
url = '/openstack/users/%s/roles' % kwargs['user']
return self._list(url, 'roles')
def add(self, user, role, tenant=None):
def add(self, user, role=None, roles=None):
"""Add a role to a user"""
# TODO: resolve the roles and users into id's
# user_id = base.getid(user)
user_id = user
# role_id = role
params = {
'roles': [role]
}
if role:
params = {
'roles': [role]
}
elif roles:
params = {
'roles': roles
}
route = '/openstack/users/%s/roles'
url = route % (user_id)
@ -82,12 +87,16 @@ class UserRolesManager(base.BaseManager):
return True
def remove(self, user_id, role_id, tenant=None):
"""Remove a role from a user"""
# TODO: perhaps support multiple roles?
params = {
'roles': [role_id]
}
def remove(self, user_id, role=None, roles=None):
"""Remove a role or roles from a user"""
if role:
params = {
'roles': [role]
}
elif roles:
params = {
'roles': roles
}
route = '/openstack/users/%s/roles'
url = route % (user_id)

View File

@ -47,16 +47,12 @@ def do_task_show(sc, args):
Get individual task.
"""
try:
task = sc.tasks.show(task_id=args.task_id)
task = sc.tasks.get(task_id=args.task_id)
formatters = {
'uuid': utils.text_wrap_formatter,
'task_type': utils.text_wrap_formatter,
'created_on': utils.text_wrap_formatter,
'approved_on': utils.text_wrap_formatter,
'completed_on': utils.text_wrap_formatter,
'actions': utils.json_formatter,
'action_notes': utils.json_formatter
'action_notes': utils.json_formatter,
'keystone_user': utils.json_formatter
}
utils.print_dict(task.to_dict(), formatters=formatters)
except exc.HTTPNotFound:
@ -70,7 +66,7 @@ def do_task_show(sc, args):
help=_('Filters to use when getting the list.'))
def do_task_list(sc, args):
"""
Show all tasks in the current tenant
Show all tasks in the current project
"""
fields = [
'uuid', 'task_type', 'created_on',
@ -132,7 +128,24 @@ def do_task_reissue_token(sc, args):
print e.message
else:
print 'Success:', ' '.join(resp.notes)
do_user_list(sc, args)
do_task_show(sc, args)
@utils.arg('task_id', metavar='<taskid>',
help=_('Task ID.'))
def do_task_cancel(sc, args):
"""
Canel the task.
"""
try:
resp = sc.tasks.cancel(args.task_id)
except exc.HTTPNotFound as e:
print e.message
except exc.HTTPBadRequest as e:
print e.message
else:
print 'Success: %s' % resp.json()['notes']
do_task_show(sc, args)
# Notifications
@ -144,13 +157,10 @@ def do_notification_show(sc, args):
Get individual notification.
"""
try:
notification = sc.notifications.show(note_id=args.note_id)
notification = sc.notifications.get(note_id=args.note_id)
formatters = {
'uuid': utils.text_wrap_formatter,
'task': utils.text_wrap_formatter,
'notes': utils.json_formatter,
'created_on': utils.text_wrap_formatter,
'notes': utils.json_formatter
}
utils.print_dict(notification.to_dict(), formatters=formatters)
except exc.HTTPNotFound:
@ -173,14 +183,14 @@ def do_notification_list(sc, args):
utils.print_list(notification_list, fields)
@utils.arg('note_id', metavar='<noteid>',
help=_('Notification ID.'))
@utils.arg('note_ids', metavar='<noteids>', nargs='+',
help=_('Notification IDs to acknowledge.'))
def do_notification_acknowledge(sc, args):
"""
Acknowledge notification.
"""
try:
resp = sc.notifications.acknowledge(note_id=args.note_id)
resp = sc.notifications.acknowledge(note_list=args.note_ids)
print 'Success:', ' '.join(resp.notes)
except exc.HTTPNotFound:
@ -213,7 +223,7 @@ def do_token_show(sc, args):
including the arguments required for submit
"""
try:
token = sc.tokens.show(args.token)
token = sc.tokens.get(args.token)
except exc.HTTPNotFound as e:
print e.message
print "Requested Token was not found."
@ -251,7 +261,7 @@ def do_token_submit(sc, args):
Submit this token to finalise Task.
"""
try:
sc.tokens.submit(args.token, args.data)
sc.tokens.submit(args.token, json.loads(args.data))
except exc.HTTPNotFound as e:
print e.message
print "Requested token was not found."
@ -262,6 +272,25 @@ def do_token_submit(sc, args):
print "Token submitted."
def do_token_clear_expired(sc, args):
"""
Clear all expired tokens.
This is an admin only endpoint.
"""
try:
resp = sc.tokens.clear_expired()
except exc.HTTPNotFound as e:
print e.message
except exc.HTTPBadRequest as e:
print e.message
else:
print 'Success: %s' % resp.json()['notes']
fields = ['token', 'task', 'created_on', 'expires']
token_list = sc.tokens.list({})
utils.print_list(token_list, fields)
# User
@utils.arg('user_id', metavar='<userid>',
@ -280,39 +309,30 @@ def do_user_show(sc, args):
def do_user_list(sc, args):
"""List all users in tenant"""
"""List all users in project"""
kwargs = {}
fields = ['id', 'email', 'name', 'roles', 'cohort', 'status']
tenant_users = sc.users.list(**kwargs)
utils.print_list(tenant_users, fields, sortby_index=1)
project_users = sc.users.list(**kwargs)
utils.print_list(project_users, fields, sortby_index=1)
@utils.arg('--roles', metavar='<roles>', nargs='+', required=True,
help=_('Roles to grant to new user'))
@utils.arg('--tenant', '--tenant-id', metavar='<tenant>', required=True,
help=_('Invite to a particular tenant id'))
@utils.arg('username', metavar='<username>', default=None,
@utils.arg('--username', metavar='<username>', default=None,
help=_('username of user to invite'))
@utils.arg('email', metavar='<email>',
@utils.arg('--email', metavar='<email>', required=True,
help=_('Email address of user to invite'))
def do_user_invite(sc, args):
"""
Invites a user to become a member of a tenant.
Invites a user to become a member of a project.
User does not need to have an existing openstack account.
"""
roles = args.roles or ['Member']
if args.tenant:
# utils.find_resource(sc.tenants, args.tenant).id
tenant_id = args.tenant
else:
tenant_id = None
try:
sc.users.invite(
username=args.username, email=args.email,
tenant_id=tenant_id, role_list=roles)
username=args.username, email=args.email, role_list=roles)
except exc.HTTPNotFound as e:
print e.message
print e
@ -320,7 +340,7 @@ def do_user_invite(sc, args):
print "400 Bad Request: " + e.message
print e
else:
print "Invitation sent. (todo: print only pending users)"
print "Invitation sent."
do_user_list(sc, args)
@ -353,28 +373,22 @@ def do_user_role_list(sc, args):
help=_('Name or ID of user.'))
@utils.arg('--role', '--role-id', metavar='<role>', required=True,
help=_('Name or ID of role.'))
@utils.arg('--tenant', '--tenant-id', metavar='<tenant>',
help=_('Name or ID of tenant.'))
def do_user_role_add(sc, args):
"""Add a role to user"""
user = utils.find_resource(sc.users, args.user)
role = utils.find_resource(sc.managed_roles, args.role)
if sc.user_roles.add(user.id, role.name):
do_user_role_list(sc, args)
if sc.user_roles.add(args.user, role=role.name):
do_user_list(sc, args)
@utils.arg('--user', '--user-id', metavar='<user>',
help=_('Name or ID of user.'))
@utils.arg('--role', '--role-id', metavar='<role>', required=True,
help=_('Name or ID of role.'))
@utils.arg('--tenant', '--tenant-id', metavar='<tenant>',
help=_('Name or ID of tenant.'))
def do_user_role_remove(sc, args):
"""Remove a role from a user"""
user = utils.find_resource(sc.users, args.user)
role = utils.find_resource(sc.managed_roles, args.role)
if sc.user_roles.remove(user.id, role.name):
do_user_role_list(sc, args)
if sc.user_roles.remove(args.user, role=role.name):
do_user_list(sc, args)
@utils.arg('email', metavar='<email>',
@ -392,7 +406,7 @@ def do_user_password_reset(sc, args):
def do_managed_role_list(sc, args):
"""List roles that may be managed in a given tenant"""
"""List roles that may be managed in a given project"""
fields = ['id', 'name']
kwargs = {}
roles = sc.managed_roles.list(**kwargs)
@ -404,5 +418,9 @@ def do_managed_role_list(sc, args):
def do_status(sc, args):
"""Requests server status endpoint and returns details of the api server"""
status = sc.status.get()
print json.dumps(status, sort_keys=True,
indent=4, separators=(',', ': '))
if status.status_code != 200:
print "Failed: %s" % status.reason
return
print json.dumps(
status.json(), sort_keys=True,
indent=4, separators=(',', ': '))

View File

@ -19,5 +19,4 @@ class StatusManager(base.BaseManager):
def get(self):
url = '/status'
body = self.client.get(url).json()
return body
return self.client.get(url)

View File

@ -24,7 +24,7 @@ class Task(base.Resource):
class TaskManager(base.ManagerWithFind):
resource_class = Task
def show(self, task_id):
def get(self, task_id):
return self._get("/tasks/%s" % base.getid(task_id))
def list(self, filters, **kwargs):

View File

@ -28,7 +28,7 @@ class TokenParam(base.Resource):
class TokenManager(base.BaseManager):
resource_class = Token
def show(self, token_id):
def get(self, token_id):
"""Get details on a particular token object"""
url = 'tokens/%s' % token_id
return self._get(url)
@ -46,13 +46,17 @@ class TokenManager(base.BaseManager):
def submit(self, token_id, parameters):
url = 'tokens/%s' % token_id
json = parameters
return self._post(url, json)
return self._post(url, parameters)
def reissue(self, task_id):
""" Given a task id, reissues the tokens associated with that task """
url = 'tokens'
json = {
data = {
'task': task_id
}
return self._post(url, json)
return self._post(url, data)
def clear_expired(self):
"""Clear all expired tokens."""
url = '/tokens?'
return self._delete(url)

View File

@ -18,9 +18,9 @@ from six.moves.urllib import parse
from stacktaskclient.openstack.common.apiclient import base
class Users(base.Resource):
class User(base.Resource):
def __repr__(self):
return "<Users %s>" % self._info
return "<User %s>" % self._info
def invite(self, **fields):
return self.manager.create(self.identifier, **fields)
@ -40,8 +40,8 @@ class Users(base.Resource):
return '%s/%s' % (self.stack_name, self.id)
class UsersManager(base.ManagerWithFind):
resource_class = Users
class UserManager(base.ManagerWithFind):
resource_class = User
def get(self, user):
return self._get("/openstack/users/%s" % base.getid(user))
@ -52,7 +52,7 @@ class UsersManager(base.ManagerWithFind):
:param limit: maximum number of users to return
:param marker: begin returning users that appear later in the user
list than that represented by this user id
:rtype: list of :class:`Users`
:rtype: list of :class:`User`
"""
def paginate(params):
'''Paginate users, even if more than API limit.'''
@ -81,13 +81,12 @@ class UsersManager(base.ManagerWithFind):
return paginate(params)
def invite(self, username, email, role_list, tenant_id=None):
""" Invite a user to the current tenant. """
def invite(self, username, email, role_list):
""" Invite a user to the current project. """
fields = {
'username': username,
'email': email,
'project_id': tenant_id,
'roles': role_list
}
self.client.post('openstack/users', data=fields)