Stacktask Client as a plugin for openstack client

All commands now available under the core openstack command and
deprecation warnings have been added to the older shell client.

Change-Id: If7b6a382a2a842a8c5085c944063544d01d58c1d
This commit is contained in:
Amelia Cordwell 2017-05-02 16:54:31 +12:00 committed by adrian-turjak
parent cf869eb1ef
commit 18a17265a8
20 changed files with 909 additions and 91 deletions

View File

@ -1,10 +0,0 @@
include AUTHORS
include babel.cfg
include LICENSE
include README.rst
include ChangeLog
include tox.ini
include .testr.conf
recursive-include doc *
recursive-include tools *
recursive-include python-stacktaskclient *.po *.pot

View File

@ -7,37 +7,25 @@ StackTask Client can be installed from PyPI using pip:
::
pip install python-stacktaskclient
pip install python-openstackclient python-stacktaskclient
There are a few variants on getting help. A list of global options and supported commands is shown with --help:
::
The command line client is installed as a plugin for the OpenStack client, and
an older deprecated version is available under the entry point 'stacktask'.
stacktask --help
Python API
==========
There is also a help command that can be used to get help text for a specific command:
In order to use the python api directly, you must first obtain an auth
token and identify which endpoint you wish to speak to::
::
>>> stacktask_url = 'http://stacktask.example.org:8004/v1/'
>>> auth_token = '3bcc3d3a03f44e3d8377f9247b0ad155'
stacktask user-invite --help
Once you have done so, you can use the API like so::
Configuration
=============
>>> from stacktaskclient.client import Client
>>> stacktask = Client('1', endpoint=stacktask_url, token=auth_token)
Authentication using username/password is most commonly used:
::
export OS_AUTH_URL=<url-to-openstack-identity>
export OS_PROJECT_NAME=<project-name>
export OS_USERNAME=<username>
export OS_PASSWORD=<password>
The corresponding command-line options look very similar:
::
--os-auth-url <url>
--os-project-name <project-name>
--os-username <username>
[--os-password <password>]
An auth token isn't needed for accessing signup, user.password_forgot(),
token.submit() or token.get().

View File

@ -3,7 +3,7 @@
# process, which may cause wedges in the gate later.
Babel>=1.3
pbr<2.0,>=1.6
pbr>=3.0.0
iso8601>=0.1.9
PrettyTable<0.8,>=0.7
oslo.i18n>=2.1.0

View File

@ -25,11 +25,39 @@ packages =
console_scripts =
stacktask = stacktaskclient.shell:main
openstack.cli.extension =
registration = stacktaskclient.osc.plugin
openstack.registration.v1 =
admin_task_list = stacktaskclient.osc.v1.tasks:TaskList
admin_task_show = stacktaskclient.osc.v1.tasks:TaskShow
admin_task_approve = stacktaskclient.osc.v1.tasks:TaskApprove
admin_task_cancel = stacktaskclient.osc.v1.tasks:TaskCancel
admin_task_update = stacktaskclient.osc.v1.tasks:TaskUpdate
admin_task_token_reissue = stacktaskclient.osc.v1.tasks:TaskTokenReissue
admin_task_token_list = stacktaskclient.osc.v1.tokens:TokenList
admin_task_token_show = stacktaskclient.osc.v1.tokens:TokenShow
admin_task_token_submit = stacktaskclient.osc.v1.tokens:TokenSubmit
admin_task_tokens_clear = stacktaskclient.osc.v1.tokens:TokenClear
admin_task_notification_list = stacktaskclient.osc.v1.notifications:NotificationList
admin_task_notification_show = stacktaskclient.osc.v1.notifications:NotificationShow
admin_task_notification_acknowledge = stacktaskclient.osc.v1.notifications:NotificationAcknowledge
project_user_list = stacktaskclient.osc.v1.users:UserList
project_user_show = stacktaskclient.osc.v1.users:UserShow
project_user_invite = stacktaskclient.osc.v1.users:UserInvite
project_user_invite_cancel = stacktaskclient.osc.v1.users:UserInviteCancel
project_user_role_list = stacktaskclient.osc.v1.users:UserRoleList
project_user_role_add = stacktaskclient.osc.v1.users:UserRoleAdd
project_user_role_remove = stacktaskclient.osc.v1.users:UserRoleRemove
project_manageable_roles = stacktaskclient.osc.v1.users:ManageableRolesList
password_forgot = stacktaskclient.osc.v1.users:PasswordForgot
password_reset = stacktaskclient.osc.v1.users:PasswordReset
signup = stacktaskclient.osc.v1.signup:Signup
stacktask_status = stacktaskclient.osc.v1.status:Status
[global]
setup-hooks =
pbr.hooks.setup_hook
[wheel]
universal = 1

View File

@ -1,35 +0,0 @@
[metadata]
name = python-stacktaskclient
summary = StackTask API Client Library
description-file =
README.md
author = Dale Smith
author-email = dale@catalyst-eu.net
home-page = https://github.com/catalyst/stacktask-client
classifier =
Environment :: OpenStack
Intended Audience :: Information Technology
Intended Audience :: System Administrators
License :: OSI Approved :: Apache Software License
Operating System :: POSIX :: Linux
Programming Language :: Python
Programming Language :: Python :: 2
Programming Language :: Python :: 2.7
Programming Language :: Python :: 2.6
[files]
packages =
stacktaskclient
[entry_points]
console_scripts =
stacktask = stacktaskclient.shell:main
[global]
setup-hooks =
pbr.hooks.setup_hook
[wheel]
universal = 1

View File

View File

@ -0,0 +1,80 @@
# Copyright 2014 OpenStack Foundation
#
# 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 logging
from osc_lib import utils
LOG = logging.getLogger(__name__)
DEFAULT_OS_REGISTRATION_VERSION = '1'
DEFAULT_API_VERSION = '1'
API_VERSION_OPTION = 'os_registration_version'
API_NAME = "registration"
API_VERSIONS = {
"1": "stacktaskclient.v1.client.Client",
}
def make_client(instance):
"""Returns an stacktask service client."""
version = instance._api_version[API_NAME]
try:
version = int(version)
except ValueError:
version = float(version)
version = 1
stacktask_client = utils.get_client_class(
API_NAME,
version,
API_VERSIONS)
LOG.debug('Instantiating stacktask client: %s', stacktask_client)
kwargs = {'region_name': instance.region_name}
if instance.session:
kwargs.update({'session': instance.session,
'service_type': API_NAME})
else:
endpoint = instance.get_endpoint_for_service_type(
API_NAME,
region_name=instance.region_name,
interface=instance.interface,
)
kwargs.update({'endpoint': endpoint,
'auth_url': instance.auth.auth_url,
'username': instance.auth_ref.username,
'token': instance.auth_ref.auth_token})
client = stacktask_client(**kwargs)
return client
def build_option_parser(parser):
"""Hook to add global options."""
parser.add_argument(
'--os-registration-version',
metavar='<registration-version>',
default=utils.env(
'OS_REGISTRATION_VERSION',
default=DEFAULT_OS_REGISTRATION_VERSION),
help=('Client version, default=' +
DEFAULT_OS_REGISTRATION_VERSION +
' (Env: DEFAULT_OS_REGISTRATION_VERSION)'))
return parser

View File

View File

@ -0,0 +1,94 @@
# Copyright (c) 2016 Catalyst IT Ltd.
#
# 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 logging
import six
import json
from osc_lib.i18n import _
from osc_lib.command import command
LOG = logging.getLogger(__name__)
def _show_notification(notification_id, client, formatter):
notification = client.notifications.get(notification_id)
if formatter == 'table':
notification._info['notes'] = json.dumps(
notification.notes, indent=2)
return zip(*six.iteritems(notification.to_dict()))
class NotificationList(command.Lister):
"""Lists stacktask notifications. """
def get_parser(self, prog_name):
parser = super(NotificationList, self).get_parser(prog_name)
parser.add_argument(
'--filters', metavar='<filters>',
required=False,
help=_('JSON containing filters for the notifications.'),
default={})
return parser
def take_action(self, parsed_args):
client = self.app.client_manager.registration
kwargs = {}
if parsed_args.filters:
kwargs['filters'] = parsed_args.filters
notifications = client.notifications.list(filters=parsed_args.filters)
headers = ['uuid', 'task', 'acknowledged', 'created_on']
rows = [[notif.uuid, notif.task, notif.acknowledged,
notif.created_on] for notif in notifications]
return headers, rows
class NotificationShow(command.ShowOne):
"""Show details of one notification."""
def get_parser(self, prog_name):
parser = super(NotificationShow, self).get_parser(prog_name)
parser.add_argument(
'notification_id', metavar='<notification_id>',
help=_("The notification ID."))
return parser
def take_action(self, parsed_args):
client = self.app.client_manager.registration
return _show_notification(parsed_args.notification_id, client,
parsed_args.formatter)
class NotificationAcknowledge(command.Command):
""" Approve and show details of a notification."""
def get_parser(self, prog_name):
parser = super(NotificationAcknowledge, self).get_parser(prog_name)
parser.add_argument('note_ids', metavar='<noteids>', nargs='+',
help=_('Notification IDs to acknowledge.'))
return parser
def take_action(self, parsed_args):
client = self.app.client_manager.registration
resp = client.notifications.acknowledge(note_list=parsed_args.note_ids)
print('Success', ' '.join(resp.notes))

View File

@ -0,0 +1,68 @@
# Copyright (c) 2016 Catalyst IT Ltd.
#
# 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 logging
import json
from osc_lib.command import command
from osc_lib.i18n import _
from stacktaskclient import client as stacktask_client
LOG = logging.getLogger(__name__)
class Signup(command.Command):
"""Submits a sign-up request."""
auth_required = False
def get_parser(self, prog_name):
parser = super(Signup, self).get_parser(prog_name)
parser.add_argument(
'--user', metavar='<user>',
required=False,
help=_('Username for new account. '
'May not be required depending on API configuration.'),
default=None)
parser.add_argument(
'email', metavar='<email>',
help=_('New account email address.'))
parser.add_argument(
'project_name', metavar='<project_name>',
help=_('Name of the new project'))
parser.add_argument(
'--bypass-url', metavar='<bypass-url>', default=None,
help=_('Bypasss URL for unauthenticated access to the endpoint.'))
parser.add_argument(
'--setup-network', action='store_true',
help=_('Create a default network during project creation.'))
return parser
def take_action(self, parsed_args):
if not parsed_args.bypass_url:
# NOTE(amelia): Assumes that this is an already authenticated
# user wanting to access and submit a sign up (I.E. an admin)
self.app.client_manager._auth_required = True
self.app.client_manager.setup_auth()
client = self.app.client_manager.registration
else:
client = stacktask_client.Client(1, parsed_args.bypass_url)
signup = client.signup.post(parsed_args.user, parsed_args.email,
parsed_args.project_name,
parsed_args.setup_network).json()
print(json.dumps(signup, indent=2))

View File

@ -0,0 +1,31 @@
# Copyright (c) 2016 Catalyst IT Ltd.
#
# 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 logging
import json
from osc_lib.command import command
LOG = logging.getLogger(__name__)
class Status(command.Command):
"""Lists stacktask tasks. """
def take_action(self, parsed_args):
client = self.app.client_manager.registration
status = client.status.get().json()
print(json.dumps(status, indent=2))

View File

@ -0,0 +1,158 @@
# Copyright (c) 2016 Catalyst IT Ltd.
#
# 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 logging
import six
import json
from osc_lib.command import command
from osc_lib.i18n import _
LOG = logging.getLogger(__name__)
def _show_task(task_id, client, formatter):
task = client.tasks.get(task_id)
if formatter == 'table':
task._info['actions'] = json.dumps(task.actions, indent=2)
task._info['keystone_user'] = json.dumps(task.keystone_user,
indent=2)
task._info['action_notes'] = json.dumps(task.action_notes,
indent=2)
task._info['approved_by'] = json.dumps(task.approved_by,
indent=2)
return zip(*six.iteritems(task.to_dict()))
class TaskList(command.Lister):
"""Lists stacktask tasks. """
def get_parser(self, prog_name):
parser = super(TaskList, self).get_parser(prog_name)
parser.add_argument(
'--limit', metavar='<limit>',
required=False,
help=_('Limit the number of task responses.'))
parser.add_argument(
'--filters', metavar='<filters>',
required=False,
help=_('JSON containing filters for the tasks.'),
default={})
return parser
def take_action(self, parsed_args):
client = self.app.client_manager.registration
kwargs = {'filters': parsed_args.filters}
if parsed_args.limit:
kwargs['tasks_per_page'] = parsed_args.limit
tasks = client.tasks.list(**kwargs)
headers = [
'UUID', 'Task Type', 'Created on',
'Approved on', 'Completed on', 'Cancelled']
rows = [[task.uuid, task.task_type, task.created_on,
task.approved_on, task.completed_on, task.cancelled]
for task in tasks]
return headers, rows
class TaskShow(command.ShowOne):
"""Show details of one task."""
def get_parser(self, prog_name):
parser = super(TaskShow, self).get_parser(prog_name)
parser.add_argument(
'task_id', metavar='<taskid>',
help=_("The task ID."))
return parser
def take_action(self, parsed_args):
client = self.app.client_manager.registration
return _show_task(parsed_args.task_id, client, parsed_args.formatter)
class TaskApprove(command.ShowOne):
""" Approve and show details of a task."""
def get_parser(self, prog_name):
parser = super(TaskApprove, self).get_parser(prog_name)
parser.add_argument(
'task_id', metavar='<taskid>',
help=_("The task ID."))
return parser
def take_action(self, parsed_args):
client = self.app.client_manager.registration
client.tasks.approve(parsed_args.task_id)
return _show_task(parsed_args.task_id, client, parsed_args.formatter)
class TaskCancel(command.ShowOne):
""" Approve and show details of a task."""
def get_parser(self, prog_name):
parser = super(TaskCancel, self).get_parser(prog_name)
parser.add_argument(
'task_id', metavar='<taskid>',
help=_("The task ID."))
return parser
def take_action(self, parsed_args):
client = self.app.client_manager.registration
client.tasks.cancel(task_id=parsed_args.task_id)
return _show_task(parsed_args.task_id, client, parsed_args.formatter)
class TaskUpdate(command.ShowOne):
"""Update data and re-run pre-approve for a task."""
def get_parser(self, prog_name):
parser = super(TaskUpdate, self).get_parser(prog_name)
parser.add_argument(
'task_id', metavar='<taskid>',
help=_("The task ID."))
parser.add_argument(
'data', metavar='<data>',
help=_("New JSON action data."))
return parser
def take_action(self, parsed_args):
client = self.app.client_manager.registration
client.tasks.update(task_id=parsed_args.task_id,
data=json.loads(parsed_args.data))
return _show_task(parsed_args.task_id, client, parsed_args.formatter)
class TaskTokenReissue(command.Command):
""" Reissues a token for the provided pending task """
def get_parser(self, prog_name):
parser = super(TaskTokenReissue, self).get_parser(prog_name)
parser.add_argument('task_id', metavar='<task_id>',
help=_('The task ID.'))
return parser
def take_action(self, parsed_args):
client = self.app.client_manager.registration
resp = client.tokens.reissue(task_id=parsed_args.task_id)
print('Success', ' '.join(resp.notes))

View File

@ -0,0 +1,107 @@
# Copyright (c) 2016 Catalyst IT Ltd.
#
# 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 logging
import json
import six
from osc_lib.command import command
from osc_lib.i18n import _
from stacktaskclient import client as stacktask_client
LOG = logging.getLogger(__name__)
def _list_tokens(client, filters={}):
tokens = client.tokens.list(filters=filters)
headers = ['Token', 'Task', 'Expires on', 'Created on']
rows = [[token.token, token.task, token.expires,
token.created_on] for token in tokens]
return headers, rows
class TokenList(command.Lister):
"""Lists stacktask tokens. """
def get_parser(self, prog_name):
parser = super(TokenList, self).get_parser(prog_name)
parser.add_argument(
'--filters', metavar='<filters>',
required=False,
help=_('JSON containing filters for tokens.'),
default={})
return parser
def take_action(self, parsed_args):
client = self.app.client_manager.registration
return _list_tokens(client, parsed_args.filters)
class TokenShow(command.ShowOne):
"""Show details of one token."""
def get_parser(self, prog_name):
parser = super(TokenShow, self).get_parser(prog_name)
parser.add_argument(
'token', metavar='<token_id>',
help=_("The token."))
parser.add_argument(
'--bypass-url', metavar='<bypass-url>', default=None,
help=_('Bypasss URL for unauthenticated access to the endpoint.'))
return parser
def take_action(self, parsed_args):
if not parsed_args.bypass_url:
self.app.client_manager._auth_required = True
self.app.client_manager.setup_auth()
client = self.app.client_manager.registration
else:
client = stacktask_client.Client("1", parsed_args.bypass_url)
token = client.tokens.get(parsed_args.token)
return zip(*six.iteritems(token.to_dict()))
class TokenSubmit(command.Command):
"""Submit token data."""
def get_parser(self, prog_name):
parser = super(TokenSubmit, self).get_parser(prog_name)
parser.add_argument(
'token', metavar='<token_id>', help=_('The token.'))
parser.add_argument(
'data', metavar='<token_data>',
help=_('Submission data for the token. Must be valid json.'))
return parser
def take_action(self, parsed_args):
client = self.app.client_manager.registration
resp = client.tokens.submit(
parsed_args.token, json.loads(parsed_args.data))
print('Success', ' '.join(resp.notes))
class TokenClear(command.Lister):
"""Clear Expired tokens, admin only."""
def take_action(self, parsed_args):
client = self.app.client_manager.registration
resp = client.tokens.clear_expired()
print('Success. ' + ' '.join(resp.json()['notes']))
return _list_tokens(client)

View File

@ -0,0 +1,246 @@
# Copyright (c) 2016 Catalyst IT Ltd.
#
# 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 logging
import six
from osc_lib import utils
from osc_lib.command import command
from osc_lib.i18n import _
from stacktaskclient import client as stacktask_client
LOG = logging.getLogger(__name__)
class UserList(command.Lister):
"""Lists users in the currently scoped project. """
def take_action(self, parsed_args):
client = self.app.client_manager.registration
project_users = client.users.list()
headers = [
'id', 'name', 'email', 'roles', 'cohort', 'status']
rows = [[user.id, user.name, user.email,
user.roles, user.cohort, user.status]
for user in project_users]
return headers, rows
class UserShow(command.ShowOne):
"""Show details of one user."""
def get_parser(self, prog_name):
parser = super(UserShow, self).get_parser(prog_name)
parser.add_argument(
'user', metavar='<user>',
help=_("The user's ID or name."))
return parser
def take_action(self, parsed_args):
client = self.app.client_manager.registration
# This ends up for names doing multiple requests, it may
# be better to do something slightly different here
user_id = utils.find_resource(client.users, parsed_args.user)
user = client.users.get(user_id)
return zip(*six.iteritems(user.to_dict()))
class UserInvite(command.Command):
"""
Invites a user to become a member of a project.
User does not need to have an existing openstack account.
"""
def get_parser(self, prog_name):
parser = super(UserInvite, self).get_parser(prog_name)
parser.add_argument(
'--username', metavar='<username>',
default=None,
help=_('The username for the new user.'))
parser.add_argument(
'email', metavar='<email>',
help=_('Email address of user to invite'))
parser.add_argument(
'roles', metavar='<role>', nargs='+',
help=_('Roles to give to the user.'))
return parser
def take_action(self, parsed_args):
if not parsed_args.roles:
parsed_args.roles = ['Member']
client = self.app.client_manager.registration
client.users.invite(
username=parsed_args.username, email=parsed_args.email,
role_list=parsed_args.roles)
print("User invited")
class UserInviteCancel(command.Command):
""" Cancel invite(s) to a project."""
def get_parser(self, prog_name):
parser = super(UserInviteCancel, self).get_parser(prog_name)
parser.add_argument(
'user', metavar='<user>',
nargs='+',
help=_("The user's name or id."))
return parser
def take_action(self, parsed_args):
client = self.app.client_manager.registration
for user in parsed_args.user:
user_id = utils.find_resource(client.users, user)
client.users.cancel(user_id=user_id)
print("Invite(s) Cancelled")
class UserRoleAdd(command.Command):
""" Add a role to a user."""
def get_parser(self, prog_name):
parser = super(UserRoleAdd, self).get_parser(prog_name)
parser.add_argument(
'user', metavar='<user>',
help=_("The user's name or id.."))
parser.add_argument(
'role', metavar='<role>',
help=_("The role's name or id."))
return parser
def take_action(self, parsed_args):
client = self.app.client_manager.registration
role = utils.find_resource(client.managed_roles, parsed_args.role)
user = utils.find_resource(client.users, parsed_args.user)
if client.user_roles.add(user.id, role=role.name):
print(_("Role added"))
class UserRoleRemove(command.Command):
""" Remove a role from a user."""
def get_parser(self, prog_name):
parser = super(UserRoleRemove, self).get_parser(prog_name)
parser.add_argument(
'user', metavar='<user>',
help=_("The user's name or id.."))
parser.add_argument(
'role', metavar='<user>',
help=_("The role's name or id."))
return parser
def take_action(self, parsed_args):
client = self.app.client_manager.registration
role = utils.find_resource(client.managed_roles, parsed_args.role)
user = utils.find_resource(client.users, parsed_args.user)
if client.user_roles.remove(user.id, role=role.name):
print(_("Role removed"))
class UserRoleList(command.Lister):
"""Lists the roles a user has on a project"""
def get_parser(self, prog_name):
parser = super(UserRoleList, self).get_parser(prog_name)
parser.add_argument(
'user', metavar='<user>',
help=_("Name or ID of user."))
return parser
def take_action(self, parsed_args):
client = self.app.client_manager.registration
user = utils.find_resource(client.users, parsed_args.user)
kwargs = {'user': user.id}
roles = [[role.id, role.name] for role
in client.user_roles.list(**kwargs)]
return ['id', 'name'], roles
class ManageableRolesList(command.Lister):
""" Lists roles able to be managed by the current user """
def take_action(self, parsed_args):
client = self.app.client_manager.registration
roles = client.managed_roles.list()
headers = ['id', 'name']
rows = [[role.id, role.name] for role in roles]
return headers, rows
class PasswordReset(command.Command):
""" Force password reset for a user, admin only. """
def get_parser(self, prog_name):
parser = super(PasswordReset, self).get_parser(prog_name)
parser.add_argument(
'email', metavar='<email>',
help=_("Email address of the user."))
parser.add_argument(
'--username', metavar='<username>', default=None,
help=_('Username of the account to reset if the username '
'is different than the email'))
return parser
def take_action(self, parsed_args):
client = self.app.client_manager.registration
data = {'email': parsed_args.email}
if parsed_args.username:
data['username'] = parsed_args.username
client.users.password_force_reset(data)
print("Task has been sucessfully submitted.")
print("If a user with that email exists, a reset "
"token will be issued.")
class PasswordForgot(command.Command):
""" Links to user forgotten password endpoint, does not require auth."""
auth_required = False
def get_parser(self, prog_name):
parser = super(PasswordForgot, self).get_parser(prog_name)
parser.add_argument(
'email', metavar='<email>',
help=_("Email address of the user."))
parser.add_argument(
'--username', metavar='<username>', default=None,
help=_('Username of the account to reset if the username '
'is different than the email'))
parser.add_argument(
'--bypass-url', metavar='<bypass-url>', default=None,
help=_('Bypasss URL for unauthenticated access to the endpoint.'))
return parser
def take_action(self, parsed_args):
if not parsed_args.bypass_url:
self.app.client_manager._auth_required = True
self.app.client_manager.setup_auth()
client = self.app.client_manager.registration
else:
client = stacktask_client.Client(1, parsed_args.bypass_url)
client.users.password_forgot(parsed_args.email, parsed_args.username)
print("Task has been sucessfully submitted.")
print("If a user with that email exists, a reset "
"token will be issued.")

View File

@ -27,14 +27,13 @@ class NotificationManager(base.ManagerWithFind):
def get(self, note_id):
return self._get("/notifications/%s" % base.getid(note_id))
def list(self, filters, **kwargs):
def list(self, **kwargs):
"""Get a list of notifications.
:rtype: list of :class:`Notification`
"""
filters = {'filters': filters}
url = '/notifications?%(params)s' % {
'params': parse.urlencode(filters, True)
'params': parse.urlencode(kwargs, True)
}
return self._list(url, 'notifications')

View File

@ -39,6 +39,14 @@ def _authenticated_fetcher(sc):
return _do
def show_deprecated(deprecated, recommended):
logger.warning('"%(old)s" is deprecated, '
'please use "%(new)s" instead',
{'old': deprecated,
'new': recommended}
)
# Tasks
@utils.arg('task_id', metavar='<taskid>',
@ -47,6 +55,8 @@ def do_task_show(sc, args):
"""
Get individual task.
"""
show_deprecated('stacktask task-show', 'openstack admin task show')
try:
task = sc.tasks.get(task_id=args.task_id)
@ -70,10 +80,11 @@ def do_task_list(sc, args):
"""
Show all tasks in the current project
"""
show_deprecated('stacktask task-list', 'openstack admin task list')
fields = [
'uuid', 'task_type', 'created_on',
'approved_on', 'completed_on', 'cancelled']
tasks_list = sc.tasks.list(args.filters)
tasks_list = sc.tasks.list(filters=args.filters)
utils.print_list(tasks_list, fields)
@ -85,6 +96,7 @@ def do_task_update(sc, args):
"""
Update a task with new data and rerun pre-approve validation.
"""
show_deprecated('stacktask task-update', 'openstack admin task update')
try:
resp = sc.tasks.update(args.task_id, args.data)
except exc.HTTPNotFound as e:
@ -103,8 +115,9 @@ def do_task_approve(sc, args):
Approve a task.
If already approved will rerun post-approve validation
and reissue/resend token.
and resend token.
"""
show_deprecated('stacktask task-approve', 'openstack admin task approve')
try:
resp = sc.tasks.approve(args.task_id)
except exc.HTTPNotFound as e:
@ -122,6 +135,8 @@ def do_task_reissue_token(sc, args):
"""
Re-issues the token for the provided pending task.
"""
show_deprecated('stacktask task-reissue-token',
'openstack admin task token reissue')
try:
resp = sc.tokens.reissue(task_id=args.task_id)
except exc.HTTPNotFound as e:
@ -139,6 +154,7 @@ def do_task_cancel(sc, args):
"""
Canel the task.
"""
show_deprecated('stacktask task-approve', 'openstack admin task cancel')
try:
resp = sc.tasks.cancel(args.task_id)
except exc.HTTPNotFound as e:
@ -158,6 +174,9 @@ def do_notification_show(sc, args):
"""
Get individual notification.
"""
show_deprecated('stacktask notification-show',
'openstack admin task notification show')
try:
notification = sc.notifications.get(note_id=args.note_id)
@ -180,8 +199,11 @@ def do_notification_list(sc, args):
This is an admin only endpoint.
"""
show_deprecated('stacktask notification-list',
'openstack admin task notificaion list')
fields = ['uuid', 'task', 'acknowledged', 'created_on']
notification_list = sc.notifications.list(args.filters)
notification_list = sc.notifications.list(filters=args.filters)
utils.print_list(notification_list, fields)
@ -191,6 +213,9 @@ def do_notification_acknowledge(sc, args):
"""
Acknowledge notification.
"""
show_deprecated('stacktask notification-acknowledge',
'openstack admin task notification acknowledge')
try:
resp = sc.notifications.acknowledge(note_list=args.note_ids)
@ -212,8 +237,10 @@ def do_token_list(sc, args):
This is an admin only endpoint.
"""
show_deprecated('stacktask token-list',
'openstack admin task token list')
fields = ['token', 'task', 'created_on', 'expires']
token_list = sc.tokens.list(args.filters)
token_list = sc.tokens.list(filters=args.filters)
utils.print_list(token_list, fields)
@ -224,6 +251,8 @@ def do_token_show(sc, args):
Show details of this token
including the arguments required for submit
"""
show_deprecated('stacktask token-show',
'openstack admin task token show')
try:
token = sc.tokens.get(args.token)
except exc.HTTPNotFound as e:
@ -241,6 +270,8 @@ def do_token_submit_password(sc, args):
"""
Submit this token to set or update your password.
"""
show_deprecated('stacktask token-submit-password',
'openstack admin task token submit')
json_data = {'password': args.password}
_token_submit(sc, args, json_data)
@ -253,6 +284,8 @@ def do_token_submit(sc, args):
"""
Submit this token to finalise Task.
"""
show_deprecated('stacktask token-submit',
'openstack admin task token submit')
try:
json_data = json.loads(args.data)
except ValueError as e:
@ -281,6 +314,8 @@ def do_token_clear_expired(sc, args):
This is an admin only endpoint.
"""
show_deprecated('stacktask token-clear-expired',
'openstack admin task token clear')
try:
resp = sc.tokens.clear_expired()
except exc.HTTPNotFound as e:
@ -302,6 +337,8 @@ def do_user_show(sc, args):
"""
Show user details.
"""
show_deprecated('stacktask user-show', 'openstack project user show')
try:
user = sc.users.get(args.user_id)
except exc.HTTPNotFound as e:
@ -313,6 +350,8 @@ def do_user_show(sc, args):
def do_user_list(sc, args):
"""List all users in project"""
show_deprecated('stacktask user-list', 'openstack project user list')
kwargs = {}
fields = ['id', 'email', 'name', 'roles', 'cohort', 'status']
@ -331,6 +370,8 @@ def do_user_invite(sc, args):
Invites a user to become a member of a project.
User does not need to have an existing openstack account.
"""
show_deprecated('stacktask user-invite', 'openstack project user invite')
roles = args.roles or ['Member']
try:
@ -351,6 +392,8 @@ def do_user_invite(sc, args):
help=_('User id for unconfirmed user.'))
def do_user_invite_cancel(sc, args):
""" Cancel user invitation. """
show_deprecated('stacktask user-invite-cancel',
'openstack project user invite cancel')
try:
resp = sc.users.cancel(args.user_id)
print 'Success: %s' % resp.json()
@ -363,6 +406,8 @@ def do_user_invite_cancel(sc, args):
help=_('Name or ID of user.'))
def do_user_role_list(sc, args):
""" List the current roles of a user"""
show_deprecated('stacktask user-role-list',
'openstack project user role list')
fields = ['id', 'name']
user = utils.find_resource(sc.users, args.user)
kwargs = {'user': user.id}
@ -376,6 +421,9 @@ def do_user_role_list(sc, args):
help=_('Name or ID of role.'))
def do_user_role_add(sc, args):
"""Add a role to user"""
show_deprecated('stacktask user-role-add',
'openstack project user role add')
role = utils.find_resource(sc.managed_roles, args.role)
user = utils.find_resource(sc.users, args.user)
if sc.user_roles.add(user.id, role=role.name):
@ -391,6 +439,9 @@ def do_user_role_add(sc, args):
help=_('Name or ID of role.'))
def do_user_role_remove(sc, args):
"""Remove a role from a user"""
show_deprecated('stacktask user-role-remove',
'openstack project user role remove')
role = utils.find_resource(sc.managed_roles, args.role)
user = utils.find_resource(sc.users, args.user)
if sc.user_roles.remove(user.id, role=role.name):
@ -403,6 +454,9 @@ def do_user_role_remove(sc, args):
@utils.arg('email', metavar='<email>',
help=_('email of the user account to reset'))
def do_user_password_forgot(sc, args):
show_deprecated('stacktask user-password-forgot',
'openstack password forgot')
"""Request a password reset email for a user."""
sc.users.password_forgot(args.email)
print "Task has been sucessfully submitted."
@ -412,6 +466,9 @@ def do_user_password_forgot(sc, args):
@utils.arg('email', metavar='<email>',
help=_('email of the user account to reset'))
def do_user_password_reset(sc, args):
show_deprecated('stacktask user-password-reset',
'openstack password-reset')
"""Force a password reset for a user. This is an admin only function."""
sc.users.password_force_reset(args.email)
print "Task has been sucessfully submitted."
@ -420,6 +477,9 @@ def do_user_password_reset(sc, args):
def do_managed_role_list(sc, args):
"""List roles that may be managed in a given project"""
show_deprecated('stacktask managed-role-list',
'openstack project manageable roles')
fields = ['id', 'name']
kwargs = {}
roles = sc.managed_roles.list(**kwargs)
@ -430,6 +490,8 @@ def do_managed_role_list(sc, args):
def do_status(sc, args):
"""Requests server status endpoint and returns details of the api server"""
show_deprecated('stacktask status', 'openstack stacktask status')
status = sc.status.get()
if status.status_code != 200:
print "Failed: %s" % status.reason
@ -452,6 +514,8 @@ def do_sign_up(sc, args):
Note: You can perform an unauthenticated request to this endpoint using
--os-no-client-auth and --bypass-url <stacktask url>
"""
show_deprecated('stacktask sign-up', 'openstack signup')
status = sc.signup.post(args.user, args.email, args.project_name)
if status.status_code != 200:
print "Failed: %s" % status.reason

View File

@ -17,11 +17,12 @@ from stacktaskclient.openstack.common.apiclient import base
class SignupManager(base.BaseManager):
def post(self, username, email, project_name):
def post(self, username, email, project_name, setup_network):
url = '/openstack/sign-up'
fields = {
'username': username,
'email': email,
'project_name': project_name
'project_name': project_name,
'setup_network': setup_network
}
return self.client.post(url, data=fields)

View File

@ -27,15 +27,13 @@ class TaskManager(base.ManagerWithFind):
def get(self, task_id):
return self._get("/tasks/%s" % base.getid(task_id))
def list(self, filters, **kwargs):
def list(self, **kwargs):
"""Get a list of tasks.
:rtype: list of :class:`Task`
"""
filters = {'filters': filters}
url = '/tasks?%(params)s' % {
'params': parse.urlencode(filters, True)
'params': parse.urlencode(kwargs, True)
}
return self._list(url, 'tasks')

View File

@ -33,14 +33,13 @@ class TokenManager(base.BaseManager):
url = 'tokens/%s' % token_id
return self._get(url)
def list(self, filters, **kwargs):
def list(self, **kwargs):
"""Get a list of tokens.
:rtype: list of :class:`Token`
"""
filters = {'filters': filters}
url = '/tokens?%(params)s' % {
'params': parse.urlencode(filters, True)
'params': parse.urlencode(kwargs, True)
}
return self._list(url, 'tokens')

View File

@ -108,9 +108,11 @@ class UserManager(base.ManagerWithFind):
# rather than an invite.
return self.cancel(user_id)
def password_forgot(self, email):
def password_forgot(self, email, username=None):
"""Forgot password email submission."""
params = {"email": email}
if username:
params['username'] = username
return self.client.post("openstack/users/password-reset",
data=params)