python-mistralclient/mistralclient/shell.py
Steve Martinelli b1614333eb remove openstack/common/cliutils.py
As part of the first community-wide goal [1], teams were asked
to remove the openstack/common package of their projects
if one existed. This was a byproduct of the old oslo-incubator
form of syncing common functionality.

The module removed in this patch, cliutils, was removed, and the
function env() was moved to shell.py since that was the only
reference to it.

Additional patches will continue to trim content from openstack/
common

[1] http://governance.openstack.org/goals/ocata/remove-incubated-oslo-code.html

Change-Id: I0d87750e6485f34272e69182914cd2659e9439b4
2016-11-08 10:54:45 -05:00

618 lines
22 KiB
Python

# Copyright 2015 - StackStorm, Inc.
#
# 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.
"""
Command-line interface to the Mistral APIs
"""
import argparse
import logging
import os
import sys
from cliff import app
from cliff import commandmanager
from osc_lib.command import command
from mistralclient.api import client
from mistralclient.auth import auth_types
import mistralclient.commands.v2.action_executions
import mistralclient.commands.v2.actions
import mistralclient.commands.v2.cron_triggers
import mistralclient.commands.v2.environments
import mistralclient.commands.v2.executions
import mistralclient.commands.v2.members
import mistralclient.commands.v2.services
import mistralclient.commands.v2.tasks
import mistralclient.commands.v2.workbooks
import mistralclient.commands.v2.workflows
from mistralclient import exceptions as exe
def env(*args, **kwargs):
"""Returns the first environment variable set.
If all are empty, defaults to '' or keyword arg `default`.
"""
for arg in args:
value = os.environ.get(arg)
if value:
return value
return kwargs.get('default', '')
class OpenStackHelpFormatter(argparse.HelpFormatter):
def __init__(self, prog, indent_increment=2, max_help_position=32,
width=None):
super(OpenStackHelpFormatter, self).__init__(
prog,
indent_increment,
max_help_position,
width
)
def start_section(self, heading):
# Title-case the headings.
heading = '%s%s' % (heading[0].upper(), heading[1:])
super(OpenStackHelpFormatter, self).start_section(heading)
class HelpAction(argparse.Action):
"""Custom help action.
Provide a custom action so the -h and --help options
to the main app will print a list of the commands.
The commands are determined by checking the CommandManager
instance, passed in as the "default" value for the action.
"""
def __call__(self, parser, namespace, values, option_string=None):
outputs = []
max_len = 0
app = self.default
parser.print_help(app.stdout)
app.stdout.write('\nCommands for API v2 :\n')
for name, ep in sorted(app.command_manager):
factory = ep.load()
cmd = factory(self, None)
one_liner = cmd.get_description().split('\n')[0]
outputs.append((name, one_liner))
max_len = max(len(name), max_len)
for (name, one_liner) in outputs:
app.stdout.write(' %s %s\n' % (name.ljust(max_len), one_liner))
sys.exit(0)
class BashCompletionCommand(command.Command):
"""Prints all of the commands and options for bash-completion."""
def take_action(self, parsed_args):
commands = set()
options = set()
for option, _action in self.app.parser._option_string_actions.items():
options.add(option)
for command_name, _cmd in self.app.command_manager:
commands.add(command_name)
print(' '.join(commands | options))
class MistralShell(app.App):
def __init__(self):
super(MistralShell, self).__init__(
description=__doc__.strip(),
version=mistralclient.__version__,
command_manager=commandmanager.CommandManager('mistral.cli'),
)
# Set v2 commands by default
self._set_shell_commands(self._get_commands_v2())
def configure_logging(self):
log_lvl = logging.DEBUG if self.options.debug else logging.WARNING
logging.basicConfig(
format="%(levelname)s (%(module)s) %(message)s",
level=log_lvl
)
logging.getLogger('iso8601').setLevel(logging.WARNING)
if self.options.verbose_level <= 1:
logging.getLogger('requests').setLevel(logging.WARNING)
def build_option_parser(self, description, version,
argparse_kwargs=None):
"""Return an argparse option parser for this application.
Subclasses may override this method to extend
the parser with more global options.
:param description: full description of the application
:paramtype description: str
:param version: version number for the application
:paramtype version: str
:param argparse_kwargs: extra keyword argument passed to the
ArgumentParser constructor
:paramtype extra_kwargs: dict
"""
argparse_kwargs = argparse_kwargs or {}
parser = argparse.ArgumentParser(
description=description,
add_help=False,
formatter_class=OpenStackHelpFormatter,
**argparse_kwargs
)
parser.add_argument(
'--version',
action='version',
version='%(prog)s {0}'.format(version),
help='Show program\'s version number and exit.'
)
parser.add_argument(
'-v', '--verbose',
action='count',
dest='verbose_level',
default=self.DEFAULT_VERBOSE_LEVEL,
help='Increase verbosity of output. Can be repeated.',
)
parser.add_argument(
'--log-file',
action='store',
default=None,
help='Specify a file to log output. Disabled by default.',
)
parser.add_argument(
'-q', '--quiet',
action='store_const',
dest='verbose_level',
const=0,
help='Suppress output except warnings and errors.',
)
parser.add_argument(
'-h', '--help',
action=HelpAction,
nargs=0,
default=self, # tricky
help="Show this help message and exit.",
)
parser.add_argument(
'--debug',
default=False,
action='store_true',
help='Show tracebacks on errors.',
)
parser.add_argument(
'--os-mistral-url',
action='store',
dest='mistral_url',
default=env('OS_MISTRAL_URL'),
help='Mistral API host (Env: OS_MISTRAL_URL)'
)
parser.add_argument(
'--os-mistral-version',
action='store',
dest='mistral_version',
default=env('OS_MISTRAL_VERSION', default='v2'),
help='Mistral API version (default = v2) (Env: '
'OS_MISTRAL_VERSION)'
)
parser.add_argument(
'--os-mistral-service-type',
action='store',
dest='service_type',
default=env('OS_MISTRAL_SERVICE_TYPE', default='workflowv2'),
help='Mistral service-type (should be the same name as in '
'keystone-endpoint) (default = workflowv2) (Env: '
'OS_MISTRAL_SERVICE_TYPE)'
)
parser.add_argument(
'--os-mistral-endpoint-type',
action='store',
dest='endpoint_type',
default=env('OS_MISTRAL_ENDPOINT_TYPE', default='publicURL'),
help='Mistral endpoint-type (should be the same name as in '
'keystone-endpoint) (default = publicURL) (Env: '
'OS_MISTRAL_ENDPOINT_TYPE)'
)
parser.add_argument(
'--os-username',
action='store',
dest='username',
default=env('OS_USERNAME'),
help='Authentication username (Env: OS_USERNAME)'
)
parser.add_argument(
'--os-password',
action='store',
dest='password',
default=env('OS_PASSWORD'),
help='Authentication password (Env: OS_PASSWORD)'
)
parser.add_argument(
'--os-tenant-id',
action='store',
dest='tenant_id',
default=env('OS_TENANT_ID'),
help='Authentication tenant identifier (Env: OS_TENANT_ID)'
)
parser.add_argument(
'--os-tenant-name',
action='store',
dest='tenant_name',
default=env('OS_TENANT_NAME', 'Default'),
help='Authentication tenant name (Env: OS_TENANT_NAME)'
)
parser.add_argument(
'--os-auth-token',
action='store',
dest='token',
default=env('OS_AUTH_TOKEN'),
help='Authentication token (Env: OS_AUTH_TOKEN)'
)
parser.add_argument(
'--os-auth-url',
action='store',
dest='auth_url',
default=env('OS_AUTH_URL'),
help='Authentication URL (Env: OS_AUTH_URL)'
)
parser.add_argument(
'--os-cert',
action='store',
dest='os_cert',
default=env('OS_CERT'),
help='Client Certificate (Env: OS_CERT)'
)
parser.add_argument(
'--os-key',
action='store',
dest='os_key',
default=env('OS_KEY'),
help='Client Key (Env: OS_KEY)'
)
parser.add_argument(
'--os-cacert',
action='store',
dest='os_cacert',
default=env('OS_CACERT'),
help='Authentication CA Certificate (Env: OS_CACERT)'
)
parser.add_argument(
'--insecure',
action='store_true',
dest='insecure',
default=env('MISTRALCLIENT_INSECURE', default=False),
help='Disables SSL/TLS certificate verification '
'(Env: MISTRALCLIENT_INSECURE)'
)
parser.add_argument(
'--auth-type',
action='store',
dest='auth_type',
default=env('MISTRAL_AUTH_TYPE', default='keystone'),
help='Authentication type. Valid options are: %s.'
' (Env: MISTRAL_AUTH_TYPE)' % auth_types.ALL
)
parser.add_argument(
'--openid-client-id',
action='store',
dest='client_id',
default=env('OPENID_CLIENT_ID'),
help='Client ID (according to OpenID Connect).'
' (Env: OPENID_CLIENT_ID)'
)
parser.add_argument(
'--openid-client-secret',
action='store',
dest='client_secret',
default=env('OPENID_CLIENT_SECRET'),
help='Client secret (according to OpenID Connect)'
' (Env: OPENID_CLIENT_SECRET)'
)
parser.add_argument(
'--os-target-username',
action='store',
dest='target_username',
default=env('OS_TARGET_USERNAME', default='admin'),
help='Authentication username for target cloud'
' (Env: OS_TARGET_USERNAME)'
)
parser.add_argument(
'--os-target-password',
action='store',
dest='target_password',
default=env('OS_TARGET_PASSWORD'),
help='Authentication password for target cloud'
' (Env: OS_TARGET_PASSWORD)'
)
parser.add_argument(
'--os-target-tenant-id',
action='store',
dest='target_tenant_id',
default=env('OS_TARGET_TENANT_ID'),
help='Authentication tenant identifier for target cloud'
' (Env: OS_TARGET_TENANT_ID)'
)
parser.add_argument(
'--os-target-tenant-name',
action='store',
dest='target_tenant_name',
default=env('OS_TARGET_TENANT_NAME', 'Default'),
help='Authentication tenant name for target cloud'
' (Env: OS_TARGET_TENANT_NAME)'
)
parser.add_argument(
'--os-target-auth-token',
action='store',
dest='target_token',
default=env('OS_TARGET_AUTH_TOKEN'),
help='Authentication token for target cloud'
' (Env: OS_TARGET_AUTH_TOKEN)'
)
parser.add_argument(
'--os-target-auth-url',
action='store',
dest='target_auth_url',
default=env('OS_TARGET_AUTH_URL'),
help='Authentication URL for target cloud'
' (Env: OS_TARGET_AUTH_URL)'
)
parser.add_argument(
'--os-target_cacert',
action='store',
dest='target_cacert',
default=env('OS_TARGET_CACERT'),
help='Authentication CA Certificate for target cloud'
' (Env: OS_TARGET_CACERT)'
)
parser.add_argument(
'--target_insecure',
action='store_true',
dest='target_insecure',
default=env('TARGET_MISTRALCLIENT_INSECURE', default=False),
help='Disables SSL/TLS certificate verification for target cloud '
'(Env: TARGET_MISTRALCLIENT_INSECURE)'
)
parser.add_argument(
'--profile',
dest='profile',
metavar='HMAC_KEY',
help='HMAC key to use for encrypting context data for performance '
'profiling of operation. This key should be one of the '
'values configured for the osprofiler middleware in mistral, '
'it is specified in the profiler section of the mistral '
'configuration (i.e. /etc/mistral/mistral.conf). Without the '
'key, profiling will not be triggered even if osprofiler is '
'enabled on the server side.'
)
return parser
def initialize_app(self, argv):
self._clear_shell_commands()
ver = client.determine_client_version(self.options.mistral_version)
self._set_shell_commands(self._get_commands(ver))
do_help = ('help' in argv) or ('-h' in argv) or not argv
# Set default for auth_url if not supplied. The default is not
# set at the parser to support use cases where auth is not enabled.
# An example use case would be a developer's environment.
if not self.options.auth_url:
if self.options.password or self.options.token:
self.options.auth_url = 'http://localhost:35357/v3'
# bash-completion should not require authentification.
if do_help or ('bash-completion' in argv):
self.options.auth_url = None
if self.options.auth_url and not self.options.token:
if not self.options.username:
raise exe.IllegalArgumentException(
("You must provide a username "
"via --os-username env[OS_USERNAME]")
)
if not self.options.password:
raise exe.IllegalArgumentException(
("You must provide a password "
"via --os-password env[OS_PASSWORD]")
)
kwargs = {
'cert': self.options.os_cert,
'key': self.options.os_key
}
self.client = client.client(
mistral_url=self.options.mistral_url,
username=self.options.username,
api_key=self.options.password,
project_name=self.options.tenant_name,
auth_url=self.options.auth_url,
project_id=self.options.tenant_id,
endpoint_type=self.options.endpoint_type,
service_type=self.options.service_type,
auth_token=self.options.token,
cacert=self.options.os_cacert,
insecure=self.options.insecure,
profile=self.options.profile,
auth_type=self.options.auth_type,
client_id=self.options.client_id,
client_secret=self.options.client_secret,
target_username=self.options.target_username,
target_api_key=self.options.target_password,
target_project_name=self.options.target_tenant_name,
target_auth_url=self.options.target_auth_url,
target_project_id=self.options.target_tenant_id,
target_auth_token=self.options.target_token,
target_cacert=self.options.target_cacert,
target_insecure=self.options.target_insecure,
**kwargs
)
# Adding client_manager variable to make mistral client work with
# unified OpenStack client.
ClientManager = type(
'ClientManager',
(object,),
dict(workflow_engine=self.client)
)
self.client_manager = ClientManager()
def _set_shell_commands(self, cmds_dict):
for k, v in cmds_dict.items():
self.command_manager.add_command(k, v)
def _clear_shell_commands(self):
exclude_cmds = ['help', 'complete']
cmds = self.command_manager.commands.copy()
for k, v in cmds.items():
if k not in exclude_cmds:
self.command_manager.commands.pop(k)
def _get_commands(self, version):
if version == 2:
return self._get_commands_v2()
return {}
@staticmethod
def _get_commands_v2():
return {
'bash-completion': BashCompletionCommand,
'workbook-list': mistralclient.commands.v2.workbooks.List,
'workbook-get': mistralclient.commands.v2.workbooks.Get,
'workbook-create': mistralclient.commands.v2.workbooks.Create,
'workbook-delete': mistralclient.commands.v2.workbooks.Delete,
'workbook-update': mistralclient.commands.v2.workbooks.Update,
'workbook-get-definition':
mistralclient.commands.v2.workbooks.GetDefinition,
'workbook-validate': mistralclient.commands.v2.workbooks.Validate,
'workflow-list': mistralclient.commands.v2.workflows.List,
'workflow-get': mistralclient.commands.v2.workflows.Get,
'workflow-create': mistralclient.commands.v2.workflows.Create,
'workflow-delete': mistralclient.commands.v2.workflows.Delete,
'workflow-update': mistralclient.commands.v2.workflows.Update,
'workflow-get-definition':
mistralclient.commands.v2.workflows.GetDefinition,
'workflow-validate': mistralclient.commands.v2.workflows.Validate,
'environment-create':
mistralclient.commands.v2.environments.Create,
'environment-delete':
mistralclient.commands.v2.environments.Delete,
'environment-update':
mistralclient.commands.v2.environments.Update,
'environment-list': mistralclient.commands.v2.environments.List,
'environment-get': mistralclient.commands.v2.environments.Get,
'run-action': mistralclient.commands.v2.action_executions.Create,
'action-execution-list':
mistralclient.commands.v2.action_executions.List,
'action-execution-get':
mistralclient.commands.v2.action_executions.Get,
'action-execution-get-input':
mistralclient.commands.v2.action_executions.GetInput,
'action-execution-get-output':
mistralclient.commands.v2.action_executions.GetOutput,
'action-execution-update':
mistralclient.commands.v2.action_executions.Update,
'action-execution-delete':
mistralclient.commands.v2.action_executions.Delete,
'execution-create': mistralclient.commands.v2.executions.Create,
'execution-delete': mistralclient.commands.v2.executions.Delete,
'execution-update': mistralclient.commands.v2.executions.Update,
'execution-list': mistralclient.commands.v2.executions.List,
'execution-get': mistralclient.commands.v2.executions.Get,
'execution-get-input':
mistralclient.commands.v2.executions.GetInput,
'execution-get-output':
mistralclient.commands.v2.executions.GetOutput,
'task-list': mistralclient.commands.v2.tasks.List,
'task-get': mistralclient.commands.v2.tasks.Get,
'task-get-published': mistralclient.commands.v2.tasks.GetPublished,
'task-get-result': mistralclient.commands.v2.tasks.GetResult,
'task-rerun': mistralclient.commands.v2.tasks.Rerun,
'action-list': mistralclient.commands.v2.actions.List,
'action-get': mistralclient.commands.v2.actions.Get,
'action-create': mistralclient.commands.v2.actions.Create,
'action-delete': mistralclient.commands.v2.actions.Delete,
'action-update': mistralclient.commands.v2.actions.Update,
'action-get-definition':
mistralclient.commands.v2.actions.GetDefinition,
'action-validate': mistralclient.commands.v2.actions.Validate,
'cron-trigger-list': mistralclient.commands.v2.cron_triggers.List,
'cron-trigger-get': mistralclient.commands.v2.cron_triggers.Get,
'cron-trigger-create':
mistralclient.commands.v2.cron_triggers.Create,
'cron-trigger-delete':
mistralclient.commands.v2.cron_triggers.Delete,
'service-list': mistralclient.commands.v2.services.List,
'member-create': mistralclient.commands.v2.members.Create,
'member-delete': mistralclient.commands.v2.members.Delete,
'member-update': mistralclient.commands.v2.members.Update,
'member-list': mistralclient.commands.v2.members.List,
'member-get': mistralclient.commands.v2.members.Get,
}
def main(argv=sys.argv[1:]):
return MistralShell().run(argv)
if __name__ == '__main__':
sys.exit(main(sys.argv[1:]))