osc-lib: shell
Convert to using ClientManager and OpenStackShell from osc-lib. * Change all internal uses of ClientManager private attributes that are now public in osc-lib's ClientManager. Leave back-compat copies in place in OSC's clientManager so we don't break plugins. * Put some work-arounds in place for changes in osc-lib that we need until a new release makes it through the g-r and u-c change process. * Add a test for Unicode decoding of argv in shell.main() to parallel the one in osc-lib. Change-Id: I85289740d4ca081f2aca8c9b40ec422ad25d302c
This commit is contained in:
parent
a42664ccaa
commit
6a15f90dae
@ -86,7 +86,7 @@ def get_plugin_modules(group):
|
|||||||
|
|
||||||
# Add the plugin to the ClientManager
|
# Add the plugin to the ClientManager
|
||||||
setattr(
|
setattr(
|
||||||
ClientManager,
|
clientmanager.ClientManager,
|
||||||
module.API_NAME,
|
module.API_NAME,
|
||||||
clientmanager.ClientCache(
|
clientmanager.ClientCache(
|
||||||
getattr(sys.modules[ep.module_name], 'make_client', None)
|
getattr(sys.modules[ep.module_name], 'make_client', None)
|
||||||
|
@ -64,7 +64,7 @@ def make_client(instance):
|
|||||||
if ext.name == "list_extensions"]
|
if ext.name == "list_extensions"]
|
||||||
|
|
||||||
# Remember interface only if it is set
|
# Remember interface only if it is set
|
||||||
kwargs = utils.build_kwargs_dict('endpoint_type', instance._interface)
|
kwargs = utils.build_kwargs_dict('endpoint_type', instance.interface)
|
||||||
|
|
||||||
client = nova_client.Client(
|
client = nova_client.Client(
|
||||||
version,
|
version,
|
||||||
@ -72,7 +72,7 @@ def make_client(instance):
|
|||||||
extensions=extensions,
|
extensions=extensions,
|
||||||
http_log_debug=http_log_debug,
|
http_log_debug=http_log_debug,
|
||||||
timings=instance.timing,
|
timings=instance.timing,
|
||||||
region_name=instance._region_name,
|
region_name=instance.region_name,
|
||||||
**kwargs
|
**kwargs
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -16,7 +16,6 @@
|
|||||||
import logging
|
import logging
|
||||||
|
|
||||||
from keystoneclient.v2_0 import client as identity_client_v2
|
from keystoneclient.v2_0 import client as identity_client_v2
|
||||||
from osc_lib.api import auth
|
|
||||||
from osc_lib import utils
|
from osc_lib import utils
|
||||||
|
|
||||||
from openstackclient.i18n import _
|
from openstackclient.i18n import _
|
||||||
@ -50,11 +49,11 @@ def make_client(instance):
|
|||||||
LOG.debug('Instantiating identity client: %s', identity_client)
|
LOG.debug('Instantiating identity client: %s', identity_client)
|
||||||
|
|
||||||
# Remember interface only if interface is set
|
# Remember interface only if interface is set
|
||||||
kwargs = utils.build_kwargs_dict('interface', instance._interface)
|
kwargs = utils.build_kwargs_dict('interface', instance.interface)
|
||||||
|
|
||||||
client = identity_client(
|
client = identity_client(
|
||||||
session=instance.session,
|
session=instance.session,
|
||||||
region_name=instance._region_name,
|
region_name=instance.region_name,
|
||||||
**kwargs
|
**kwargs
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -70,7 +69,7 @@ def build_option_parser(parser):
|
|||||||
help=_('Identity API version, default=%s '
|
help=_('Identity API version, default=%s '
|
||||||
'(Env: OS_IDENTITY_API_VERSION)') % DEFAULT_API_VERSION,
|
'(Env: OS_IDENTITY_API_VERSION)') % DEFAULT_API_VERSION,
|
||||||
)
|
)
|
||||||
return auth.build_auth_plugins_option_parser(parser)
|
return parser
|
||||||
|
|
||||||
|
|
||||||
class IdentityClientv2(identity_client_v2.Client):
|
class IdentityClientv2(identity_client_v2.Client):
|
||||||
|
@ -47,15 +47,15 @@ def make_client(instance):
|
|||||||
|
|
||||||
endpoint = instance.get_endpoint_for_service_type(
|
endpoint = instance.get_endpoint_for_service_type(
|
||||||
API_NAME,
|
API_NAME,
|
||||||
region_name=instance._region_name,
|
region_name=instance.region_name,
|
||||||
interface=instance._interface,
|
interface=instance.interface,
|
||||||
)
|
)
|
||||||
|
|
||||||
client = image_client(
|
client = image_client(
|
||||||
endpoint,
|
endpoint,
|
||||||
token=instance.auth.get_token(instance.session),
|
token=instance.auth.get_token(instance.session),
|
||||||
cacert=instance._cacert,
|
cacert=instance.cacert,
|
||||||
insecure=instance._insecure,
|
insecure=not instance.verify,
|
||||||
)
|
)
|
||||||
|
|
||||||
# Create the low-level API
|
# Create the low-level API
|
||||||
@ -70,8 +70,8 @@ def make_client(instance):
|
|||||||
session=instance.session,
|
session=instance.session,
|
||||||
endpoint=instance.get_endpoint_for_service_type(
|
endpoint=instance.get_endpoint_for_service_type(
|
||||||
IMAGE_API_TYPE,
|
IMAGE_API_TYPE,
|
||||||
region_name=instance._region_name,
|
region_name=instance.region_name,
|
||||||
interface=instance._interface,
|
interface=instance.interface,
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -34,9 +34,9 @@ API_VERSIONS = {
|
|||||||
def make_client(instance):
|
def make_client(instance):
|
||||||
"""Returns a network proxy"""
|
"""Returns a network proxy"""
|
||||||
prof = profile.Profile()
|
prof = profile.Profile()
|
||||||
prof.set_region(API_NAME, instance._region_name)
|
prof.set_region(API_NAME, instance.region_name)
|
||||||
prof.set_version(API_NAME, instance._api_version[API_NAME])
|
prof.set_version(API_NAME, instance._api_version[API_NAME])
|
||||||
prof.set_interface(API_NAME, instance._interface)
|
prof.set_interface(API_NAME, instance.interface)
|
||||||
conn = connection.Connection(authenticator=instance.session.auth,
|
conn = connection.Connection(authenticator=instance.session.auth,
|
||||||
verify=instance.session.verify,
|
verify=instance.session.verify,
|
||||||
cert=instance.session.cert,
|
cert=instance.session.cert,
|
||||||
|
@ -32,8 +32,8 @@ def make_client(instance):
|
|||||||
|
|
||||||
endpoint = instance.get_endpoint_for_service_type(
|
endpoint = instance.get_endpoint_for_service_type(
|
||||||
'object-store',
|
'object-store',
|
||||||
region_name=instance._region_name,
|
region_name=instance.region_name,
|
||||||
interface=instance._interface,
|
interface=instance.interface,
|
||||||
)
|
)
|
||||||
|
|
||||||
client = object_store_v1.APIv1(
|
client = object_store_v1.APIv1(
|
||||||
|
@ -16,30 +16,17 @@
|
|||||||
|
|
||||||
"""Command-line interface to the OpenStack APIs"""
|
"""Command-line interface to the OpenStack APIs"""
|
||||||
|
|
||||||
import argparse
|
|
||||||
import getpass
|
|
||||||
import locale
|
import locale
|
||||||
import logging
|
|
||||||
import six
|
|
||||||
import sys
|
import sys
|
||||||
import traceback
|
|
||||||
|
|
||||||
from cliff import app
|
from osc_lib.api import auth
|
||||||
from cliff import command
|
from osc_lib import shell
|
||||||
from cliff import complete
|
|
||||||
from cliff import help
|
|
||||||
from osc_lib.cli import client_config as cloud_config
|
|
||||||
from osc_lib.command import timing
|
|
||||||
from osc_lib import exceptions as exc
|
|
||||||
from osc_lib import logs
|
|
||||||
from osc_lib import utils
|
|
||||||
from oslo_utils import importutils
|
from oslo_utils import importutils
|
||||||
from oslo_utils import strutils
|
import six
|
||||||
|
|
||||||
import openstackclient
|
import openstackclient
|
||||||
from openstackclient.common import clientmanager
|
from openstackclient.common import clientmanager
|
||||||
from openstackclient.common import commandmanager
|
from openstackclient.common import commandmanager
|
||||||
from openstackclient.i18n import _
|
|
||||||
|
|
||||||
osprofiler_profiler = importutils.try_import("osprofiler.profiler")
|
osprofiler_profiler = importutils.try_import("osprofiler.profiler")
|
||||||
|
|
||||||
@ -47,47 +34,9 @@ osprofiler_profiler = importutils.try_import("osprofiler.profiler")
|
|||||||
DEFAULT_DOMAIN = 'default'
|
DEFAULT_DOMAIN = 'default'
|
||||||
|
|
||||||
|
|
||||||
def prompt_for_password(prompt=None):
|
class OpenStackShell(shell.OpenStackShell):
|
||||||
"""Prompt user for a password
|
|
||||||
|
|
||||||
Prompt for a password if stdin is a tty.
|
|
||||||
"""
|
|
||||||
|
|
||||||
if not prompt:
|
|
||||||
prompt = 'Password: '
|
|
||||||
pw = None
|
|
||||||
# If stdin is a tty, try prompting for the password
|
|
||||||
if hasattr(sys.stdin, 'isatty') and sys.stdin.isatty():
|
|
||||||
# Check for Ctl-D
|
|
||||||
try:
|
|
||||||
pw = getpass.getpass(prompt)
|
|
||||||
except EOFError:
|
|
||||||
pass
|
|
||||||
# No password because we did't have a tty or nothing was entered
|
|
||||||
if not pw:
|
|
||||||
raise exc.CommandError(_("No password entered, or found via"
|
|
||||||
" --os-password or OS_PASSWORD"),)
|
|
||||||
return pw
|
|
||||||
|
|
||||||
|
|
||||||
class OpenStackShell(app.App):
|
|
||||||
|
|
||||||
CONSOLE_MESSAGE_FORMAT = '%(levelname)s: %(name)s %(message)s'
|
|
||||||
|
|
||||||
log = logging.getLogger(__name__)
|
|
||||||
timing_data = []
|
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
# Patch command.Command to add a default auth_required = True
|
|
||||||
command.Command.auth_required = True
|
|
||||||
|
|
||||||
# Some commands do not need authentication
|
|
||||||
help.HelpCommand.auth_required = False
|
|
||||||
complete.CompleteCommand.auth_required = False
|
|
||||||
|
|
||||||
# Slight change to the meaning of --debug
|
|
||||||
self.DEFAULT_DEBUG_VALUE = None
|
|
||||||
self.DEFAULT_DEBUG_HELP = 'Set debug logging and traceback on errors.'
|
|
||||||
|
|
||||||
super(OpenStackShell, self).__init__(
|
super(OpenStackShell, self).__init__(
|
||||||
description=__doc__.strip(),
|
description=__doc__.strip(),
|
||||||
@ -97,281 +46,28 @@ class OpenStackShell(app.App):
|
|||||||
|
|
||||||
self.api_version = {}
|
self.api_version = {}
|
||||||
|
|
||||||
# Until we have command line arguments parsed, dump any stack traces
|
|
||||||
self.dump_stack_trace = True
|
|
||||||
|
|
||||||
# Assume TLS host certificate verification is enabled
|
# Assume TLS host certificate verification is enabled
|
||||||
self.verify = True
|
self.verify = True
|
||||||
|
|
||||||
self.client_manager = None
|
|
||||||
self.command_options = None
|
|
||||||
|
|
||||||
self.do_profile = False
|
|
||||||
|
|
||||||
def configure_logging(self):
|
|
||||||
"""Configure logging for the app."""
|
|
||||||
self.log_configurator = logs.LogConfigurator(self.options)
|
|
||||||
self.dump_stack_trace = self.log_configurator.dump_trace
|
|
||||||
|
|
||||||
def run(self, argv):
|
|
||||||
ret_val = 1
|
|
||||||
self.command_options = argv
|
|
||||||
try:
|
|
||||||
ret_val = super(OpenStackShell, self).run(argv)
|
|
||||||
return ret_val
|
|
||||||
except Exception as e:
|
|
||||||
if not logging.getLogger('').handlers:
|
|
||||||
logging.basicConfig()
|
|
||||||
if self.dump_stack_trace:
|
|
||||||
self.log.error(traceback.format_exc())
|
|
||||||
else:
|
|
||||||
self.log.error('Exception raised: ' + str(e))
|
|
||||||
|
|
||||||
return ret_val
|
|
||||||
|
|
||||||
finally:
|
|
||||||
self.log.info("END return value: %s", ret_val)
|
|
||||||
|
|
||||||
def init_profile(self):
|
|
||||||
# NOTE(dtroyer): Remove this 'if' block when the --profile global
|
|
||||||
# option is removed
|
|
||||||
if osprofiler_profiler and self.options.old_profile:
|
|
||||||
self.log.warning(
|
|
||||||
'The --profile option is deprecated, '
|
|
||||||
'please use --os-profile instead'
|
|
||||||
)
|
|
||||||
if not self.options.profile:
|
|
||||||
self.options.profile = self.options.old_profile
|
|
||||||
|
|
||||||
self.do_profile = osprofiler_profiler and self.options.profile
|
|
||||||
if self.do_profile:
|
|
||||||
osprofiler_profiler.init(self.options.profile)
|
|
||||||
|
|
||||||
def close_profile(self):
|
|
||||||
if self.do_profile:
|
|
||||||
trace_id = osprofiler_profiler.get().get_base_id()
|
|
||||||
|
|
||||||
# NOTE(dbelova): let's use warning log level to see these messages
|
|
||||||
# printed. In fact we can define custom log level here with value
|
|
||||||
# bigger than most big default one (CRITICAL) or something like
|
|
||||||
# that (PROFILE = 60 for instance), but not sure we need it here.
|
|
||||||
self.log.warning("Trace ID: %s" % trace_id)
|
|
||||||
self.log.warning("Display trace with command:\n"
|
|
||||||
"osprofiler trace show --html %s " % trace_id)
|
|
||||||
|
|
||||||
def run_subcommand(self, argv):
|
|
||||||
self.init_profile()
|
|
||||||
try:
|
|
||||||
ret_value = super(OpenStackShell, self).run_subcommand(argv)
|
|
||||||
finally:
|
|
||||||
self.close_profile()
|
|
||||||
return ret_value
|
|
||||||
|
|
||||||
def interact(self):
|
|
||||||
self.init_profile()
|
|
||||||
try:
|
|
||||||
ret_value = super(OpenStackShell, self).interact()
|
|
||||||
finally:
|
|
||||||
self.close_profile()
|
|
||||||
return ret_value
|
|
||||||
|
|
||||||
def build_option_parser(self, description, version):
|
def build_option_parser(self, description, version):
|
||||||
parser = super(OpenStackShell, self).build_option_parser(
|
parser = super(OpenStackShell, self).build_option_parser(
|
||||||
description,
|
description,
|
||||||
version)
|
version)
|
||||||
|
parser = clientmanager.build_plugin_option_parser(parser)
|
||||||
|
parser = auth.build_auth_plugins_option_parser(parser)
|
||||||
|
return parser
|
||||||
|
|
||||||
# service token auth argument
|
def _final_defaults(self):
|
||||||
parser.add_argument(
|
super(OpenStackShell, self)._final_defaults()
|
||||||
'--os-cloud',
|
|
||||||
metavar='<cloud-config-name>',
|
|
||||||
dest='cloud',
|
|
||||||
default=utils.env('OS_CLOUD'),
|
|
||||||
help=_('Cloud name in clouds.yaml (Env: OS_CLOUD)'),
|
|
||||||
)
|
|
||||||
# Global arguments
|
|
||||||
parser.add_argument(
|
|
||||||
'--os-region-name',
|
|
||||||
metavar='<auth-region-name>',
|
|
||||||
dest='region_name',
|
|
||||||
default=utils.env('OS_REGION_NAME'),
|
|
||||||
help=_('Authentication region name (Env: OS_REGION_NAME)'),
|
|
||||||
)
|
|
||||||
parser.add_argument(
|
|
||||||
'--os-cacert',
|
|
||||||
metavar='<ca-bundle-file>',
|
|
||||||
dest='cacert',
|
|
||||||
default=utils.env('OS_CACERT'),
|
|
||||||
help=_('CA certificate bundle file (Env: OS_CACERT)'),
|
|
||||||
)
|
|
||||||
parser.add_argument(
|
|
||||||
'--os-cert',
|
|
||||||
metavar='<certificate-file>',
|
|
||||||
dest='cert',
|
|
||||||
default=utils.env('OS_CERT'),
|
|
||||||
help=_('Client certificate bundle file (Env: OS_CERT)'),
|
|
||||||
)
|
|
||||||
parser.add_argument(
|
|
||||||
'--os-key',
|
|
||||||
metavar='<key-file>',
|
|
||||||
dest='key',
|
|
||||||
default=utils.env('OS_KEY'),
|
|
||||||
help=_('Client certificate key file (Env: OS_KEY)'),
|
|
||||||
)
|
|
||||||
verify_group = parser.add_mutually_exclusive_group()
|
|
||||||
verify_group.add_argument(
|
|
||||||
'--verify',
|
|
||||||
action='store_true',
|
|
||||||
default=None,
|
|
||||||
help=_('Verify server certificate (default)'),
|
|
||||||
)
|
|
||||||
verify_group.add_argument(
|
|
||||||
'--insecure',
|
|
||||||
action='store_true',
|
|
||||||
default=None,
|
|
||||||
help=_('Disable server certificate verification'),
|
|
||||||
)
|
|
||||||
parser.add_argument(
|
|
||||||
'--os-default-domain',
|
|
||||||
metavar='<auth-domain>',
|
|
||||||
dest='default_domain',
|
|
||||||
default=utils.env(
|
|
||||||
'OS_DEFAULT_DOMAIN',
|
|
||||||
default=DEFAULT_DOMAIN),
|
|
||||||
help=_('Default domain ID, default=%s. '
|
|
||||||
'(Env: OS_DEFAULT_DOMAIN)') % DEFAULT_DOMAIN,
|
|
||||||
)
|
|
||||||
parser.add_argument(
|
|
||||||
'--os-interface',
|
|
||||||
metavar='<interface>',
|
|
||||||
dest='interface',
|
|
||||||
choices=['admin', 'public', 'internal'],
|
|
||||||
default=utils.env('OS_INTERFACE'),
|
|
||||||
help=_('Select an interface type.'
|
|
||||||
' Valid interface types: [admin, public, internal].'
|
|
||||||
' (Env: OS_INTERFACE)'),
|
|
||||||
)
|
|
||||||
parser.add_argument(
|
|
||||||
'--timing',
|
|
||||||
default=False,
|
|
||||||
action='store_true',
|
|
||||||
help=_("Print API call timing info"),
|
|
||||||
)
|
|
||||||
parser.add_argument(
|
|
||||||
'--os-beta-command',
|
|
||||||
action='store_true',
|
|
||||||
help=_("Enable beta commands which are subject to change"),
|
|
||||||
)
|
|
||||||
|
|
||||||
# osprofiler HMAC key argument
|
# Set default auth type to password
|
||||||
if osprofiler_profiler:
|
self._auth_type = 'password'
|
||||||
parser.add_argument(
|
|
||||||
'--os-profile',
|
|
||||||
metavar='hmac-key',
|
|
||||||
dest='profile',
|
|
||||||
help=_('HMAC key for encrypting profiling context data'),
|
|
||||||
)
|
|
||||||
# NOTE(dtroyer): This global option should have been named
|
|
||||||
# --os-profile as --profile interferes with at
|
|
||||||
# least one existing command option. Deprecate
|
|
||||||
# --profile and remove after Apr 2017.
|
|
||||||
parser.add_argument(
|
|
||||||
'--profile',
|
|
||||||
metavar='hmac-key',
|
|
||||||
dest='old_profile',
|
|
||||||
help=argparse.SUPPRESS,
|
|
||||||
)
|
|
||||||
|
|
||||||
return clientmanager.build_plugin_option_parser(parser)
|
def _load_plugins(self):
|
||||||
|
"""Load plugins via stevedore
|
||||||
|
|
||||||
def initialize_app(self, argv):
|
osc-lib has no opinion on what plugins should be loaded
|
||||||
"""Global app init bits:
|
|
||||||
|
|
||||||
* set up API versions
|
|
||||||
* validate authentication info
|
|
||||||
* authenticate against Identity if requested
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# Parent __init__ parses argv into self.options
|
|
||||||
super(OpenStackShell, self).initialize_app(argv)
|
|
||||||
self.log.info("START with options: %s",
|
|
||||||
strutils.mask_password(self.command_options))
|
|
||||||
self.log.debug("options: %s",
|
|
||||||
strutils.mask_password(self.options))
|
|
||||||
|
|
||||||
# Set the default plugin to token_endpoint if url and token are given
|
|
||||||
if (self.options.url and self.options.token):
|
|
||||||
# Use service token authentication
|
|
||||||
auth_type = 'token_endpoint'
|
|
||||||
else:
|
|
||||||
auth_type = 'password'
|
|
||||||
|
|
||||||
project_id = getattr(self.options, 'project_id', None)
|
|
||||||
project_name = getattr(self.options, 'project_name', None)
|
|
||||||
tenant_id = getattr(self.options, 'tenant_id', None)
|
|
||||||
tenant_name = getattr(self.options, 'tenant_name', None)
|
|
||||||
|
|
||||||
# Save default domain
|
|
||||||
self.default_domain = self.options.default_domain
|
|
||||||
|
|
||||||
# handle some v2/v3 authentication inconsistencies by just acting like
|
|
||||||
# both the project and tenant information are both present. This can
|
|
||||||
# go away if we stop registering all the argparse options together.
|
|
||||||
if project_id and not tenant_id:
|
|
||||||
self.options.tenant_id = project_id
|
|
||||||
if project_name and not tenant_name:
|
|
||||||
self.options.tenant_name = project_name
|
|
||||||
if tenant_id and not project_id:
|
|
||||||
self.options.project_id = tenant_id
|
|
||||||
if tenant_name and not project_name:
|
|
||||||
self.options.project_name = tenant_name
|
|
||||||
|
|
||||||
# Do configuration file handling
|
|
||||||
# Ignore the default value of interface. Only if it is set later
|
|
||||||
# will it be used.
|
|
||||||
try:
|
|
||||||
cc = cloud_config.OSC_Config(
|
|
||||||
override_defaults={
|
|
||||||
'interface': None,
|
|
||||||
'auth_type': auth_type,
|
|
||||||
},
|
|
||||||
)
|
|
||||||
except (IOError, OSError):
|
|
||||||
self.log.critical("Could not read clouds.yaml configuration file")
|
|
||||||
self.print_help_if_requested()
|
|
||||||
raise
|
|
||||||
|
|
||||||
# TODO(thowe): Change cliff so the default value for debug
|
|
||||||
# can be set to None.
|
|
||||||
if not self.options.debug:
|
|
||||||
self.options.debug = None
|
|
||||||
self.cloud = cc.get_one_cloud(
|
|
||||||
cloud=self.options.cloud,
|
|
||||||
argparse=self.options,
|
|
||||||
)
|
|
||||||
|
|
||||||
self.log_configurator.configure(self.cloud)
|
|
||||||
self.dump_stack_trace = self.log_configurator.dump_trace
|
|
||||||
self.log.debug("defaults: %s", cc.defaults)
|
|
||||||
self.log.debug("cloud cfg: %s",
|
|
||||||
strutils.mask_password(self.cloud.config))
|
|
||||||
|
|
||||||
# Set up client TLS
|
|
||||||
# NOTE(dtroyer): --insecure is the non-default condition that
|
|
||||||
# overrides any verify setting in clouds.yaml
|
|
||||||
# so check it first, then fall back to any verify
|
|
||||||
# setting provided.
|
|
||||||
self.verify = not self.cloud.config.get(
|
|
||||||
'insecure',
|
|
||||||
not self.cloud.config.get('verify', True),
|
|
||||||
)
|
|
||||||
|
|
||||||
# NOTE(dtroyer): Per bug https://bugs.launchpad.net/bugs/1447784
|
|
||||||
# --insecure now overrides any --os-cacert setting,
|
|
||||||
# where before --insecure was ignored if --os-cacert
|
|
||||||
# was set.
|
|
||||||
if self.verify and self.cloud.cacert:
|
|
||||||
self.verify = self.cloud.cacert
|
|
||||||
|
|
||||||
# Loop through extensions to get API versions
|
# Loop through extensions to get API versions
|
||||||
for mod in clientmanager.PLUGIN_MODULES:
|
for mod in clientmanager.PLUGIN_MODULES:
|
||||||
default_version = getattr(mod, 'DEFAULT_API_VERSION', None)
|
default_version = getattr(mod, 'DEFAULT_API_VERSION', None)
|
||||||
@ -406,6 +102,11 @@ class OpenStackShell(app.App):
|
|||||||
{'name': api, 'version': version_opt, 'group': cmd_group}
|
{'name': api, 'version': version_opt, 'group': cmd_group}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def _load_commands(self):
|
||||||
|
"""Load commands via cliff/stevedore
|
||||||
|
|
||||||
|
osc-lib has no opinion on what commands should be loaded
|
||||||
|
"""
|
||||||
# Commands that span multiple APIs
|
# Commands that span multiple APIs
|
||||||
self.command_manager.add_command_group(
|
self.command_manager.add_command_group(
|
||||||
'openstack.common')
|
'openstack.common')
|
||||||
@ -422,59 +123,19 @@ class OpenStackShell(app.App):
|
|||||||
# }
|
# }
|
||||||
self.command_manager.add_command_group(
|
self.command_manager.add_command_group(
|
||||||
'openstack.extension')
|
'openstack.extension')
|
||||||
# call InitializeXxx() here
|
|
||||||
# set up additional clients to stuff in to client_manager??
|
|
||||||
|
|
||||||
# Handle deferred help and exit
|
def initialize_app(self, argv):
|
||||||
self.print_help_if_requested()
|
super(OpenStackShell, self).initialize_app(argv)
|
||||||
|
|
||||||
|
# For now we need to build our own ClientManager so re-do what
|
||||||
|
# has already been done :(
|
||||||
|
# TODO(dtroyer): remove when osc-lib is fixed
|
||||||
self.client_manager = clientmanager.ClientManager(
|
self.client_manager = clientmanager.ClientManager(
|
||||||
cli_options=self.cloud,
|
cli_options=self.cloud,
|
||||||
api_version=self.api_version,
|
api_version=self.api_version,
|
||||||
pw_func=prompt_for_password,
|
pw_func=shell.prompt_for_password,
|
||||||
)
|
)
|
||||||
|
|
||||||
def prepare_to_run_command(self, cmd):
|
|
||||||
"""Set up auth and API versions"""
|
|
||||||
self.log.info(
|
|
||||||
'command: %s -> %s.%s',
|
|
||||||
getattr(cmd, 'cmd_name', '<none>'),
|
|
||||||
cmd.__class__.__module__,
|
|
||||||
cmd.__class__.__name__,
|
|
||||||
)
|
|
||||||
if cmd.auth_required:
|
|
||||||
self.client_manager.setup_auth()
|
|
||||||
if hasattr(cmd, 'required_scope') and cmd.required_scope:
|
|
||||||
# let the command decide whether we need a scoped token
|
|
||||||
self.client_manager.validate_scope()
|
|
||||||
# Trigger the Identity client to initialize
|
|
||||||
self.client_manager.auth_ref
|
|
||||||
|
|
||||||
def clean_up(self, cmd, result, err):
|
|
||||||
self.log.debug('clean_up %s: %s', cmd.__class__.__name__, err or '')
|
|
||||||
|
|
||||||
# Process collected timing data
|
|
||||||
if self.options.timing:
|
|
||||||
# Get session data
|
|
||||||
self.timing_data.extend(
|
|
||||||
self.client_manager.session.get_timings(),
|
|
||||||
)
|
|
||||||
|
|
||||||
# Use the Timing pseudo-command to generate the output
|
|
||||||
tcmd = timing.Timing(self, self.options)
|
|
||||||
tparser = tcmd.get_parser('Timing')
|
|
||||||
|
|
||||||
# If anything other than prettytable is specified, force csv
|
|
||||||
format = 'table'
|
|
||||||
# Check the formatter used in the actual command
|
|
||||||
if hasattr(cmd, 'formatter') \
|
|
||||||
and cmd.formatter != cmd._formatter_plugins['table'].obj:
|
|
||||||
format = 'csv'
|
|
||||||
|
|
||||||
sys.stdout.write('\n')
|
|
||||||
targs = tparser.parse_args(['-f', format])
|
|
||||||
tcmd.run(targs)
|
|
||||||
|
|
||||||
|
|
||||||
def main(argv=None):
|
def main(argv=None):
|
||||||
if argv is None:
|
if argv is None:
|
||||||
|
@ -13,14 +13,15 @@
|
|||||||
# under the License.
|
# under the License.
|
||||||
#
|
#
|
||||||
|
|
||||||
import copy
|
|
||||||
import fixtures
|
|
||||||
import mock
|
import mock
|
||||||
import os
|
import os
|
||||||
import testtools
|
import sys
|
||||||
|
|
||||||
|
from osc_lib.tests import utils as osc_lib_test_utils
|
||||||
|
from oslo_utils import importutils
|
||||||
|
import wrapt
|
||||||
|
|
||||||
from openstackclient import shell
|
from openstackclient import shell
|
||||||
from openstackclient.tests import utils
|
|
||||||
|
|
||||||
|
|
||||||
DEFAULT_AUTH_URL = "http://127.0.0.1:5000/v2.0/"
|
DEFAULT_AUTH_URL = "http://127.0.0.1:5000/v2.0/"
|
||||||
@ -116,155 +117,50 @@ global_options = {
|
|||||||
'--os-interface': (DEFAULT_INTERFACE, True, True)
|
'--os-interface': (DEFAULT_INTERFACE, True, True)
|
||||||
}
|
}
|
||||||
|
|
||||||
auth_options = {
|
|
||||||
'--os-auth-url': (DEFAULT_AUTH_URL, True, True),
|
# Wrap the osc_lib make_shell() function to set the shell class since
|
||||||
'--os-project-id': (DEFAULT_PROJECT_ID, True, True),
|
# osc-lib's TestShell class doesn't allow us to specify it yet.
|
||||||
'--os-project-name': (DEFAULT_PROJECT_NAME, True, True),
|
# TODO(dtroyer): remove this once the shell_class_patch patch is released
|
||||||
'--os-domain-id': (DEFAULT_DOMAIN_ID, True, True),
|
# in osc-lib
|
||||||
'--os-domain-name': (DEFAULT_DOMAIN_NAME, True, True),
|
def make_shell_wrapper(func, inst, args, kwargs):
|
||||||
'--os-user-domain-id': (DEFAULT_USER_DOMAIN_ID, True, True),
|
if 'shell_class' not in kwargs:
|
||||||
'--os-user-domain-name': (DEFAULT_USER_DOMAIN_NAME, True, True),
|
kwargs['shell_class'] = shell.OpenStackShell
|
||||||
'--os-project-domain-id': (DEFAULT_PROJECT_DOMAIN_ID, True, True),
|
return func(*args, **kwargs)
|
||||||
'--os-project-domain-name': (DEFAULT_PROJECT_DOMAIN_NAME, True, True),
|
|
||||||
'--os-username': (DEFAULT_USERNAME, True, True),
|
|
||||||
'--os-password': (DEFAULT_PASSWORD, True, True),
|
|
||||||
'--os-region-name': (DEFAULT_REGION_NAME, True, True),
|
|
||||||
'--os-trust-id': ("1234", True, True),
|
|
||||||
'--os-auth-type': ("v2password", True, True),
|
|
||||||
'--os-token': (DEFAULT_TOKEN, True, True),
|
|
||||||
'--os-url': (DEFAULT_SERVICE_URL, True, True),
|
|
||||||
'--os-interface': (DEFAULT_INTERFACE, True, True),
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
def opt2attr(opt):
|
wrapt.wrap_function_wrapper(
|
||||||
if opt.startswith('--os-'):
|
osc_lib_test_utils,
|
||||||
attr = opt[5:]
|
'make_shell',
|
||||||
elif opt.startswith('--'):
|
make_shell_wrapper,
|
||||||
attr = opt[2:]
|
)
|
||||||
else:
|
|
||||||
attr = opt
|
|
||||||
return attr.lower().replace('-', '_')
|
|
||||||
|
|
||||||
|
|
||||||
def opt2env(opt):
|
class TestShell(osc_lib_test_utils.TestShell):
|
||||||
return opt[2:].upper().replace('-', '_')
|
|
||||||
|
|
||||||
|
# Full name of the OpenStackShell class to test (cliff.app.App subclass)
|
||||||
|
shell_class_name = "openstackclient.shell.OpenStackShell"
|
||||||
|
|
||||||
def make_shell():
|
# TODO(dtroyer): remove this once the shell_class_patch patch is released
|
||||||
"""Create a new command shell and mock out some bits."""
|
# in osc-lib
|
||||||
_shell = shell.OpenStackShell()
|
app_patch = shell_class_name
|
||||||
_shell.command_manager = mock.Mock()
|
|
||||||
|
|
||||||
return _shell
|
|
||||||
|
|
||||||
|
|
||||||
def fake_execute(shell, cmd):
|
|
||||||
"""Pretend to execute shell commands."""
|
|
||||||
return shell.run(cmd.split())
|
|
||||||
|
|
||||||
|
|
||||||
class EnvFixture(fixtures.Fixture):
|
|
||||||
"""Environment Fixture.
|
|
||||||
|
|
||||||
This fixture replaces os.environ with provided env or an empty env.
|
|
||||||
"""
|
|
||||||
|
|
||||||
def __init__(self, env=None):
|
|
||||||
self.new_env = env or {}
|
|
||||||
|
|
||||||
def _setUp(self):
|
|
||||||
self.orig_env, os.environ = os.environ, self.new_env
|
|
||||||
self.addCleanup(self.revert)
|
|
||||||
|
|
||||||
def revert(self):
|
|
||||||
os.environ = self.orig_env
|
|
||||||
|
|
||||||
|
|
||||||
class TestShell(utils.TestCase):
|
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
super(TestShell, self).setUp()
|
super(TestShell, self).setUp()
|
||||||
patch = "openstackclient.shell.OpenStackShell.run_subcommand"
|
# TODO(dtroyer): remove this once the shell_class_patch patch is
|
||||||
self.cmd_patch = mock.patch(patch)
|
# released in osc-lib
|
||||||
self.cmd_save = self.cmd_patch.start()
|
self.shell_class = importutils.import_class(self.shell_class_name)
|
||||||
self.addCleanup(self.cmd_patch.stop)
|
|
||||||
self.app = mock.Mock("Test Shell")
|
|
||||||
|
|
||||||
def _assert_initialize_app_arg(self, cmd_options, default_args):
|
|
||||||
"""Check the args passed to initialize_app()
|
|
||||||
|
|
||||||
The argv argument to initialize_app() is the remainder from parsing
|
|
||||||
global options declared in both cliff.app and
|
|
||||||
openstackclient.OpenStackShell build_option_parser(). Any global
|
|
||||||
options passed on the commmad line should not be in argv but in
|
|
||||||
_shell.options.
|
|
||||||
"""
|
|
||||||
|
|
||||||
with mock.patch(
|
|
||||||
"openstackclient.shell.OpenStackShell.initialize_app",
|
|
||||||
self.app,
|
|
||||||
):
|
|
||||||
_shell, _cmd = make_shell(), cmd_options + " list project"
|
|
||||||
fake_execute(_shell, _cmd)
|
|
||||||
|
|
||||||
self.app.assert_called_with(["list", "project"])
|
|
||||||
for k in default_args.keys():
|
|
||||||
self.assertEqual(
|
|
||||||
default_args[k],
|
|
||||||
vars(_shell.options)[k],
|
|
||||||
"%s does not match" % k,
|
|
||||||
)
|
|
||||||
|
|
||||||
def _assert_cloud_config_arg(self, cmd_options, default_args):
|
|
||||||
"""Check the args passed to cloud_config.get_one_cloud()
|
|
||||||
|
|
||||||
The argparse argument to get_one_cloud() is an argparse.Namespace
|
|
||||||
object that contains all of the options processed to this point in
|
|
||||||
initialize_app().
|
|
||||||
"""
|
|
||||||
|
|
||||||
cloud = mock.Mock(name="cloudy")
|
|
||||||
cloud.config = {}
|
|
||||||
self.occ_get_one = mock.Mock(return_value=cloud)
|
|
||||||
with mock.patch(
|
|
||||||
"os_client_config.config.OpenStackConfig.get_one_cloud",
|
|
||||||
self.occ_get_one,
|
|
||||||
):
|
|
||||||
_shell, _cmd = make_shell(), cmd_options + " list project"
|
|
||||||
fake_execute(_shell, _cmd)
|
|
||||||
|
|
||||||
opts = self.occ_get_one.call_args[1]['argparse']
|
|
||||||
for k in default_args.keys():
|
|
||||||
self.assertEqual(
|
|
||||||
default_args[k],
|
|
||||||
vars(opts)[k],
|
|
||||||
"%s does not match" % k,
|
|
||||||
)
|
|
||||||
|
|
||||||
def _assert_token_auth(self, cmd_options, default_args):
|
|
||||||
with mock.patch("openstackclient.shell.OpenStackShell.initialize_app",
|
|
||||||
self.app):
|
|
||||||
_shell, _cmd = make_shell(), cmd_options + " list role"
|
|
||||||
fake_execute(_shell, _cmd)
|
|
||||||
|
|
||||||
self.app.assert_called_with(["list", "role"])
|
|
||||||
self.assertEqual(
|
|
||||||
default_args.get("token", ''),
|
|
||||||
_shell.options.token,
|
|
||||||
"token"
|
|
||||||
)
|
|
||||||
self.assertEqual(
|
|
||||||
default_args.get("auth_url", ''),
|
|
||||||
_shell.options.auth_url,
|
|
||||||
"auth_url"
|
|
||||||
)
|
|
||||||
|
|
||||||
def _assert_token_endpoint_auth(self, cmd_options, default_args):
|
def _assert_token_endpoint_auth(self, cmd_options, default_args):
|
||||||
with mock.patch("openstackclient.shell.OpenStackShell.initialize_app",
|
with mock.patch(
|
||||||
self.app):
|
self.shell_class_name + ".initialize_app",
|
||||||
_shell, _cmd = make_shell(), cmd_options + " list role"
|
self.app,
|
||||||
fake_execute(_shell, _cmd)
|
):
|
||||||
|
_shell = osc_lib_test_utils.make_shell(
|
||||||
|
shell_class=self.shell_class,
|
||||||
|
)
|
||||||
|
_cmd = cmd_options + " list role"
|
||||||
|
osc_lib_test_utils.fake_execute(_shell, _cmd)
|
||||||
|
print("_shell: %s" % _shell)
|
||||||
|
|
||||||
self.app.assert_called_with(["list", "role"])
|
self.app.assert_called_with(["list", "role"])
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
@ -278,11 +174,40 @@ class TestShell(utils.TestCase):
|
|||||||
"url",
|
"url",
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def _assert_token_auth(self, cmd_options, default_args):
|
||||||
|
with mock.patch(
|
||||||
|
self.app_patch + ".initialize_app",
|
||||||
|
self.app,
|
||||||
|
):
|
||||||
|
_shell = osc_lib_test_utils.make_shell(
|
||||||
|
shell_class=self.shell_class,
|
||||||
|
)
|
||||||
|
_cmd = cmd_options + " list role"
|
||||||
|
osc_lib_test_utils.fake_execute(_shell, _cmd)
|
||||||
|
print("_shell: %s" % _shell)
|
||||||
|
|
||||||
|
self.app.assert_called_with(["list", "role"])
|
||||||
|
self.assertEqual(
|
||||||
|
default_args.get("token", ''),
|
||||||
|
_shell.options.token,
|
||||||
|
"token"
|
||||||
|
)
|
||||||
|
self.assertEqual(
|
||||||
|
default_args.get("auth_url", ''),
|
||||||
|
_shell.options.auth_url,
|
||||||
|
"auth_url"
|
||||||
|
)
|
||||||
|
|
||||||
def _assert_cli(self, cmd_options, default_args):
|
def _assert_cli(self, cmd_options, default_args):
|
||||||
with mock.patch("openstackclient.shell.OpenStackShell.initialize_app",
|
with mock.patch(
|
||||||
self.app):
|
self.shell_class_name + ".initialize_app",
|
||||||
_shell, _cmd = make_shell(), cmd_options + " list server"
|
self.app,
|
||||||
fake_execute(_shell, _cmd)
|
):
|
||||||
|
_shell = osc_lib_test_utils.make_shell(
|
||||||
|
shell_class=self.shell_class,
|
||||||
|
)
|
||||||
|
_cmd = cmd_options + " list server"
|
||||||
|
osc_lib_test_utils.fake_execute(_shell, _cmd)
|
||||||
|
|
||||||
self.app.assert_called_with(["list", "server"])
|
self.app.assert_called_with(["list", "server"])
|
||||||
self.assertEqual(default_args["compute_api_version"],
|
self.assertEqual(default_args["compute_api_version"],
|
||||||
@ -297,39 +222,17 @@ class TestShell(utils.TestCase):
|
|||||||
_shell.options.os_network_api_version)
|
_shell.options.os_network_api_version)
|
||||||
|
|
||||||
|
|
||||||
class TestShellHelp(TestShell):
|
|
||||||
"""Test the deferred help flag"""
|
|
||||||
|
|
||||||
def setUp(self):
|
|
||||||
super(TestShellHelp, self).setUp()
|
|
||||||
self.useFixture(EnvFixture())
|
|
||||||
|
|
||||||
@testtools.skip("skip until bug 1444983 is resolved")
|
|
||||||
def test_help_options(self):
|
|
||||||
flag = "-h list server"
|
|
||||||
kwargs = {
|
|
||||||
"deferred_help": True,
|
|
||||||
}
|
|
||||||
with mock.patch("openstackclient.shell.OpenStackShell.initialize_app",
|
|
||||||
self.app):
|
|
||||||
_shell, _cmd = make_shell(), flag
|
|
||||||
fake_execute(_shell, _cmd)
|
|
||||||
|
|
||||||
self.assertEqual(kwargs["deferred_help"],
|
|
||||||
_shell.options.deferred_help)
|
|
||||||
|
|
||||||
|
|
||||||
class TestShellOptions(TestShell):
|
class TestShellOptions(TestShell):
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
super(TestShellOptions, self).setUp()
|
super(TestShellOptions, self).setUp()
|
||||||
self.useFixture(EnvFixture())
|
self.useFixture(osc_lib_test_utils.EnvFixture())
|
||||||
|
|
||||||
def _test_options_init_app(self, test_opts):
|
def _test_options_init_app(self, test_opts):
|
||||||
for opt in test_opts.keys():
|
for opt in test_opts.keys():
|
||||||
if not test_opts[opt][1]:
|
if not test_opts[opt][1]:
|
||||||
continue
|
continue
|
||||||
key = opt2attr(opt)
|
key = osc_lib_test_utils.opt2attr(opt)
|
||||||
if isinstance(test_opts[opt][0], str):
|
if isinstance(test_opts[opt][0], str):
|
||||||
cmd = opt + " " + test_opts[opt][0]
|
cmd = opt + " " + test_opts[opt][0]
|
||||||
else:
|
else:
|
||||||
@ -343,7 +246,7 @@ class TestShellOptions(TestShell):
|
|||||||
for opt in test_opts.keys():
|
for opt in test_opts.keys():
|
||||||
if not test_opts[opt][1]:
|
if not test_opts[opt][1]:
|
||||||
continue
|
continue
|
||||||
key = opt2attr(opt)
|
key = osc_lib_test_utils.opt2attr(opt)
|
||||||
if isinstance(test_opts[opt][0], str):
|
if isinstance(test_opts[opt][0], str):
|
||||||
cmd = opt + " " + test_opts[opt][0]
|
cmd = opt + " " + test_opts[opt][0]
|
||||||
else:
|
else:
|
||||||
@ -357,12 +260,12 @@ class TestShellOptions(TestShell):
|
|||||||
for opt in test_opts.keys():
|
for opt in test_opts.keys():
|
||||||
if not test_opts[opt][2]:
|
if not test_opts[opt][2]:
|
||||||
continue
|
continue
|
||||||
key = opt2attr(opt)
|
key = osc_lib_test_utils.opt2attr(opt)
|
||||||
kwargs = {
|
kwargs = {
|
||||||
key: test_opts[opt][0],
|
key: test_opts[opt][0],
|
||||||
}
|
}
|
||||||
env = {
|
env = {
|
||||||
opt2env(opt): test_opts[opt][0],
|
osc_lib_test_utils.opt2env(opt): test_opts[opt][0],
|
||||||
}
|
}
|
||||||
os.environ = env.copy()
|
os.environ = env.copy()
|
||||||
self._assert_initialize_app_arg("", kwargs)
|
self._assert_initialize_app_arg("", kwargs)
|
||||||
@ -371,37 +274,16 @@ class TestShellOptions(TestShell):
|
|||||||
for opt in test_opts.keys():
|
for opt in test_opts.keys():
|
||||||
if not test_opts[opt][2]:
|
if not test_opts[opt][2]:
|
||||||
continue
|
continue
|
||||||
key = opt2attr(opt)
|
key = osc_lib_test_utils.opt2attr(opt)
|
||||||
kwargs = {
|
kwargs = {
|
||||||
key: test_opts[opt][0],
|
key: test_opts[opt][0],
|
||||||
}
|
}
|
||||||
env = {
|
env = {
|
||||||
opt2env(opt): test_opts[opt][0],
|
osc_lib_test_utils.opt2env(opt): test_opts[opt][0],
|
||||||
}
|
}
|
||||||
os.environ = env.copy()
|
os.environ = env.copy()
|
||||||
self._assert_cloud_config_arg("", kwargs)
|
self._assert_cloud_config_arg("", kwargs)
|
||||||
|
|
||||||
def test_empty_auth(self):
|
|
||||||
os.environ = {}
|
|
||||||
self._assert_initialize_app_arg("", {})
|
|
||||||
self._assert_cloud_config_arg("", {})
|
|
||||||
|
|
||||||
def test_global_options(self):
|
|
||||||
self._test_options_init_app(global_options)
|
|
||||||
self._test_options_get_one_cloud(global_options)
|
|
||||||
|
|
||||||
def test_auth_options(self):
|
|
||||||
self._test_options_init_app(auth_options)
|
|
||||||
self._test_options_get_one_cloud(auth_options)
|
|
||||||
|
|
||||||
def test_global_env(self):
|
|
||||||
self._test_env_init_app(global_options)
|
|
||||||
self._test_env_get_one_cloud(global_options)
|
|
||||||
|
|
||||||
def test_auth_env(self):
|
|
||||||
self._test_env_init_app(auth_options)
|
|
||||||
self._test_env_get_one_cloud(auth_options)
|
|
||||||
|
|
||||||
|
|
||||||
class TestShellTokenAuthEnv(TestShell):
|
class TestShellTokenAuthEnv(TestShell):
|
||||||
|
|
||||||
@ -411,7 +293,7 @@ class TestShellTokenAuthEnv(TestShell):
|
|||||||
"OS_TOKEN": DEFAULT_TOKEN,
|
"OS_TOKEN": DEFAULT_TOKEN,
|
||||||
"OS_AUTH_URL": DEFAULT_AUTH_URL,
|
"OS_AUTH_URL": DEFAULT_AUTH_URL,
|
||||||
}
|
}
|
||||||
self.useFixture(EnvFixture(env.copy()))
|
self.useFixture(osc_lib_test_utils.EnvFixture(env.copy()))
|
||||||
|
|
||||||
def test_env(self):
|
def test_env(self):
|
||||||
flag = ""
|
flag = ""
|
||||||
@ -455,7 +337,7 @@ class TestShellTokenEndpointAuthEnv(TestShell):
|
|||||||
"OS_TOKEN": DEFAULT_TOKEN,
|
"OS_TOKEN": DEFAULT_TOKEN,
|
||||||
"OS_URL": DEFAULT_SERVICE_URL,
|
"OS_URL": DEFAULT_SERVICE_URL,
|
||||||
}
|
}
|
||||||
self.useFixture(EnvFixture(env.copy()))
|
self.useFixture(osc_lib_test_utils.EnvFixture(env.copy()))
|
||||||
|
|
||||||
def test_env(self):
|
def test_env(self):
|
||||||
flag = ""
|
flag = ""
|
||||||
@ -463,7 +345,7 @@ class TestShellTokenEndpointAuthEnv(TestShell):
|
|||||||
"token": DEFAULT_TOKEN,
|
"token": DEFAULT_TOKEN,
|
||||||
"url": DEFAULT_SERVICE_URL,
|
"url": DEFAULT_SERVICE_URL,
|
||||||
}
|
}
|
||||||
self._assert_token_auth(flag, kwargs)
|
self._assert_token_endpoint_auth(flag, kwargs)
|
||||||
|
|
||||||
def test_only_token(self):
|
def test_only_token(self):
|
||||||
flag = "--os-token xyzpdq"
|
flag = "--os-token xyzpdq"
|
||||||
@ -502,85 +384,7 @@ class TestShellCli(TestShell):
|
|||||||
"OS_VOLUME_API_VERSION": DEFAULT_VOLUME_API_VERSION,
|
"OS_VOLUME_API_VERSION": DEFAULT_VOLUME_API_VERSION,
|
||||||
"OS_NETWORK_API_VERSION": DEFAULT_NETWORK_API_VERSION,
|
"OS_NETWORK_API_VERSION": DEFAULT_NETWORK_API_VERSION,
|
||||||
}
|
}
|
||||||
self.useFixture(EnvFixture(env.copy()))
|
self.useFixture(osc_lib_test_utils.EnvFixture(env.copy()))
|
||||||
|
|
||||||
def test_shell_args_no_options(self):
|
|
||||||
_shell = make_shell()
|
|
||||||
with mock.patch("openstackclient.shell.OpenStackShell.initialize_app",
|
|
||||||
self.app):
|
|
||||||
fake_execute(_shell, "list user")
|
|
||||||
self.app.assert_called_with(["list", "user"])
|
|
||||||
|
|
||||||
def test_shell_args_ca_options(self):
|
|
||||||
_shell = make_shell()
|
|
||||||
|
|
||||||
# NOTE(dtroyer): The commented out asserts below are the desired
|
|
||||||
# behaviour and will be uncommented when the
|
|
||||||
# handling for --verify and --insecure is fixed.
|
|
||||||
|
|
||||||
# Default
|
|
||||||
fake_execute(_shell, "list user")
|
|
||||||
self.assertIsNone(_shell.options.verify)
|
|
||||||
self.assertIsNone(_shell.options.insecure)
|
|
||||||
self.assertEqual('', _shell.options.cacert)
|
|
||||||
self.assertTrue(_shell.verify)
|
|
||||||
|
|
||||||
# --verify
|
|
||||||
fake_execute(_shell, "--verify list user")
|
|
||||||
self.assertTrue(_shell.options.verify)
|
|
||||||
self.assertIsNone(_shell.options.insecure)
|
|
||||||
self.assertEqual('', _shell.options.cacert)
|
|
||||||
self.assertTrue(_shell.verify)
|
|
||||||
|
|
||||||
# --insecure
|
|
||||||
fake_execute(_shell, "--insecure list user")
|
|
||||||
self.assertIsNone(_shell.options.verify)
|
|
||||||
self.assertTrue(_shell.options.insecure)
|
|
||||||
self.assertEqual('', _shell.options.cacert)
|
|
||||||
self.assertFalse(_shell.verify)
|
|
||||||
|
|
||||||
# --os-cacert
|
|
||||||
fake_execute(_shell, "--os-cacert foo list user")
|
|
||||||
self.assertIsNone(_shell.options.verify)
|
|
||||||
self.assertIsNone(_shell.options.insecure)
|
|
||||||
self.assertEqual('foo', _shell.options.cacert)
|
|
||||||
self.assertTrue(_shell.verify)
|
|
||||||
|
|
||||||
# --os-cacert and --verify
|
|
||||||
fake_execute(_shell, "--os-cacert foo --verify list user")
|
|
||||||
self.assertTrue(_shell.options.verify)
|
|
||||||
self.assertIsNone(_shell.options.insecure)
|
|
||||||
self.assertEqual('foo', _shell.options.cacert)
|
|
||||||
self.assertTrue(_shell.verify)
|
|
||||||
|
|
||||||
# --os-cacert and --insecure
|
|
||||||
# NOTE(dtroyer): Per bug https://bugs.launchpad.net/bugs/1447784
|
|
||||||
# in this combination --insecure now overrides any
|
|
||||||
# --os-cacert setting, where before --insecure
|
|
||||||
# was ignored if --os-cacert was set.
|
|
||||||
fake_execute(_shell, "--os-cacert foo --insecure list user")
|
|
||||||
self.assertIsNone(_shell.options.verify)
|
|
||||||
self.assertTrue(_shell.options.insecure)
|
|
||||||
self.assertEqual('foo', _shell.options.cacert)
|
|
||||||
self.assertFalse(_shell.verify)
|
|
||||||
|
|
||||||
def test_shell_args_cert_options(self):
|
|
||||||
_shell = make_shell()
|
|
||||||
|
|
||||||
# Default
|
|
||||||
fake_execute(_shell, "list user")
|
|
||||||
self.assertEqual('', _shell.options.cert)
|
|
||||||
self.assertEqual('', _shell.options.key)
|
|
||||||
|
|
||||||
# --os-cert
|
|
||||||
fake_execute(_shell, "--os-cert mycert list user")
|
|
||||||
self.assertEqual('mycert', _shell.options.cert)
|
|
||||||
self.assertEqual('', _shell.options.key)
|
|
||||||
|
|
||||||
# --os-key
|
|
||||||
fake_execute(_shell, "--os-key mickey list user")
|
|
||||||
self.assertEqual('', _shell.options.cert)
|
|
||||||
self.assertEqual('mickey', _shell.options.key)
|
|
||||||
|
|
||||||
def test_default_env(self):
|
def test_default_env(self):
|
||||||
flag = ""
|
flag = ""
|
||||||
@ -605,220 +409,34 @@ class TestShellCli(TestShell):
|
|||||||
}
|
}
|
||||||
self._assert_cli(flag, kwargs)
|
self._assert_cli(flag, kwargs)
|
||||||
|
|
||||||
@mock.patch("os_client_config.config.OpenStackConfig._load_config_file")
|
|
||||||
def test_shell_args_cloud_no_vendor(self, config_mock):
|
|
||||||
config_mock.return_value = ('file.yaml', copy.deepcopy(CLOUD_1))
|
|
||||||
_shell = make_shell()
|
|
||||||
|
|
||||||
fake_execute(
|
class TestShellArgV(TestShell):
|
||||||
_shell,
|
"""Test the deferred help flag"""
|
||||||
"--os-cloud scc list user",
|
|
||||||
)
|
|
||||||
self.assertEqual(
|
|
||||||
'scc',
|
|
||||||
_shell.cloud.name,
|
|
||||||
)
|
|
||||||
|
|
||||||
# These come from clouds.yaml
|
|
||||||
self.assertEqual(
|
|
||||||
DEFAULT_AUTH_URL,
|
|
||||||
_shell.cloud.config['auth']['auth_url'],
|
|
||||||
)
|
|
||||||
self.assertEqual(
|
|
||||||
DEFAULT_PROJECT_NAME,
|
|
||||||
_shell.cloud.config['auth']['project_name'],
|
|
||||||
)
|
|
||||||
self.assertEqual(
|
|
||||||
'zaphod',
|
|
||||||
_shell.cloud.config['auth']['username'],
|
|
||||||
)
|
|
||||||
self.assertEqual(
|
|
||||||
'occ-cloud',
|
|
||||||
_shell.cloud.config['region_name'],
|
|
||||||
)
|
|
||||||
self.assertEqual(
|
|
||||||
'glazed',
|
|
||||||
_shell.cloud.config['donut'],
|
|
||||||
)
|
|
||||||
self.assertEqual(
|
|
||||||
'public',
|
|
||||||
_shell.cloud.config['interface'],
|
|
||||||
)
|
|
||||||
|
|
||||||
@mock.patch("os_client_config.config.OpenStackConfig._load_vendor_file")
|
|
||||||
@mock.patch("os_client_config.config.OpenStackConfig._load_config_file")
|
|
||||||
def test_shell_args_cloud_public(self, config_mock, public_mock):
|
|
||||||
config_mock.return_value = ('file.yaml', copy.deepcopy(CLOUD_2))
|
|
||||||
public_mock.return_value = ('file.yaml', copy.deepcopy(PUBLIC_1))
|
|
||||||
_shell = make_shell()
|
|
||||||
|
|
||||||
fake_execute(
|
|
||||||
_shell,
|
|
||||||
"--os-cloud megacloud list user",
|
|
||||||
)
|
|
||||||
self.assertEqual(
|
|
||||||
'megacloud',
|
|
||||||
_shell.cloud.name,
|
|
||||||
)
|
|
||||||
|
|
||||||
# These come from clouds-public.yaml
|
|
||||||
self.assertEqual(
|
|
||||||
DEFAULT_AUTH_URL,
|
|
||||||
_shell.cloud.config['auth']['auth_url'],
|
|
||||||
)
|
|
||||||
self.assertEqual(
|
|
||||||
'cake',
|
|
||||||
_shell.cloud.config['donut'],
|
|
||||||
)
|
|
||||||
|
|
||||||
# These come from clouds.yaml
|
|
||||||
self.assertEqual(
|
|
||||||
'heart-o-gold',
|
|
||||||
_shell.cloud.config['auth']['project_name'],
|
|
||||||
)
|
|
||||||
self.assertEqual(
|
|
||||||
'zaphod',
|
|
||||||
_shell.cloud.config['auth']['username'],
|
|
||||||
)
|
|
||||||
self.assertEqual(
|
|
||||||
'occ-cloud',
|
|
||||||
_shell.cloud.config['region_name'],
|
|
||||||
)
|
|
||||||
|
|
||||||
self.assertEqual('mycert', _shell.cloud.config['cert'])
|
|
||||||
self.assertEqual('mickey', _shell.cloud.config['key'])
|
|
||||||
|
|
||||||
@mock.patch("os_client_config.config.OpenStackConfig._load_vendor_file")
|
|
||||||
@mock.patch("os_client_config.config.OpenStackConfig._load_config_file")
|
|
||||||
def test_shell_args_precedence(self, config_mock, vendor_mock):
|
|
||||||
config_mock.return_value = ('file.yaml', copy.deepcopy(CLOUD_2))
|
|
||||||
vendor_mock.return_value = ('file.yaml', copy.deepcopy(PUBLIC_1))
|
|
||||||
_shell = make_shell()
|
|
||||||
|
|
||||||
# Test command option overriding config file value
|
|
||||||
fake_execute(
|
|
||||||
_shell,
|
|
||||||
"--os-cloud megacloud --os-region-name krikkit list user",
|
|
||||||
)
|
|
||||||
self.assertEqual(
|
|
||||||
'megacloud',
|
|
||||||
_shell.cloud.name,
|
|
||||||
)
|
|
||||||
|
|
||||||
# These come from clouds-public.yaml
|
|
||||||
self.assertEqual(
|
|
||||||
DEFAULT_AUTH_URL,
|
|
||||||
_shell.cloud.config['auth']['auth_url'],
|
|
||||||
)
|
|
||||||
self.assertEqual(
|
|
||||||
'cake',
|
|
||||||
_shell.cloud.config['donut'],
|
|
||||||
)
|
|
||||||
|
|
||||||
# These come from clouds.yaml
|
|
||||||
self.assertEqual(
|
|
||||||
'heart-o-gold',
|
|
||||||
_shell.cloud.config['auth']['project_name'],
|
|
||||||
)
|
|
||||||
self.assertEqual(
|
|
||||||
'zaphod',
|
|
||||||
_shell.cloud.config['auth']['username'],
|
|
||||||
)
|
|
||||||
self.assertEqual(
|
|
||||||
'krikkit',
|
|
||||||
_shell.cloud.config['region_name'],
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
class TestShellCliEnv(TestShell):
|
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
super(TestShellCliEnv, self).setUp()
|
super(TestShellArgV, self).setUp()
|
||||||
env = {
|
|
||||||
'OS_REGION_NAME': 'occ-env',
|
|
||||||
}
|
|
||||||
self.useFixture(EnvFixture(env.copy()))
|
|
||||||
|
|
||||||
@mock.patch("os_client_config.config.OpenStackConfig._load_vendor_file")
|
def test_shell_argv(self):
|
||||||
@mock.patch("os_client_config.config.OpenStackConfig._load_config_file")
|
"""Test argv decoding
|
||||||
def test_shell_args_precedence_1(self, config_mock, vendor_mock):
|
|
||||||
config_mock.return_value = ('file.yaml', copy.deepcopy(CLOUD_2))
|
|
||||||
vendor_mock.return_value = ('file.yaml', copy.deepcopy(PUBLIC_1))
|
|
||||||
_shell = make_shell()
|
|
||||||
|
|
||||||
# Test env var
|
Python 2 does nothing with argv while Python 3 decodes it into
|
||||||
fake_execute(
|
Unicode before we ever see it. We manually decode when running
|
||||||
_shell,
|
under Python 2 so verify that we get the right argv types.
|
||||||
"--os-cloud megacloud list user",
|
|
||||||
)
|
|
||||||
self.assertEqual(
|
|
||||||
'megacloud',
|
|
||||||
_shell.cloud.name,
|
|
||||||
)
|
|
||||||
|
|
||||||
# These come from clouds-public.yaml
|
Use the argv supplied by the test runner so we get actual Python
|
||||||
self.assertEqual(
|
runtime behaviour; we only need to check the type of argv[0]
|
||||||
DEFAULT_AUTH_URL,
|
which will alwyas be present.
|
||||||
_shell.cloud.config['auth']['auth_url'],
|
"""
|
||||||
)
|
|
||||||
self.assertEqual(
|
|
||||||
'cake',
|
|
||||||
_shell.cloud.config['donut'],
|
|
||||||
)
|
|
||||||
|
|
||||||
# These come from clouds.yaml
|
with mock.patch(
|
||||||
self.assertEqual(
|
self.shell_class_name + ".run",
|
||||||
'heart-o-gold',
|
self.app,
|
||||||
_shell.cloud.config['auth']['project_name'],
|
):
|
||||||
)
|
# Ensure type gets through unmolested through shell.main()
|
||||||
self.assertEqual(
|
argv = sys.argv
|
||||||
'zaphod',
|
shell.main(sys.argv)
|
||||||
_shell.cloud.config['auth']['username'],
|
self.assertEqual(type(argv[0]), type(self.app.call_args[0][0][0]))
|
||||||
)
|
|
||||||
self.assertEqual(
|
|
||||||
'occ-env',
|
|
||||||
_shell.cloud.config['region_name'],
|
|
||||||
)
|
|
||||||
|
|
||||||
@mock.patch("os_client_config.config.OpenStackConfig._load_vendor_file")
|
# When shell.main() gets sys.argv itself it should be decoded
|
||||||
@mock.patch("os_client_config.config.OpenStackConfig._load_config_file")
|
shell.main()
|
||||||
def test_shell_args_precedence_2(self, config_mock, vendor_mock):
|
self.assertEqual(type(u'x'), type(self.app.call_args[0][0][0]))
|
||||||
config_mock.return_value = ('file.yaml', copy.deepcopy(CLOUD_2))
|
|
||||||
vendor_mock.return_value = ('file.yaml', copy.deepcopy(PUBLIC_1))
|
|
||||||
_shell = make_shell()
|
|
||||||
|
|
||||||
# Test command option overriding config file value
|
|
||||||
fake_execute(
|
|
||||||
_shell,
|
|
||||||
"--os-cloud megacloud --os-region-name krikkit list user",
|
|
||||||
)
|
|
||||||
self.assertEqual(
|
|
||||||
'megacloud',
|
|
||||||
_shell.cloud.name,
|
|
||||||
)
|
|
||||||
|
|
||||||
# These come from clouds-public.yaml
|
|
||||||
self.assertEqual(
|
|
||||||
DEFAULT_AUTH_URL,
|
|
||||||
_shell.cloud.config['auth']['auth_url'],
|
|
||||||
)
|
|
||||||
self.assertEqual(
|
|
||||||
'cake',
|
|
||||||
_shell.cloud.config['donut'],
|
|
||||||
)
|
|
||||||
|
|
||||||
# These come from clouds.yaml
|
|
||||||
self.assertEqual(
|
|
||||||
'heart-o-gold',
|
|
||||||
_shell.cloud.config['auth']['project_name'],
|
|
||||||
)
|
|
||||||
self.assertEqual(
|
|
||||||
'zaphod',
|
|
||||||
_shell.cloud.config['auth']['username'],
|
|
||||||
)
|
|
||||||
|
|
||||||
# These come from the command line
|
|
||||||
self.assertEqual(
|
|
||||||
'krikkit',
|
|
||||||
_shell.cloud.config['region_name'],
|
|
||||||
)
|
|
||||||
|
@ -57,13 +57,13 @@ def make_client(instance):
|
|||||||
extensions = [extension.Extension('list_extensions', list_extensions)]
|
extensions = [extension.Extension('list_extensions', list_extensions)]
|
||||||
|
|
||||||
# Remember interface only if it is set
|
# Remember interface only if it is set
|
||||||
kwargs = utils.build_kwargs_dict('endpoint_type', instance._interface)
|
kwargs = utils.build_kwargs_dict('endpoint_type', instance.interface)
|
||||||
|
|
||||||
client = volume_client(
|
client = volume_client(
|
||||||
session=instance.session,
|
session=instance.session,
|
||||||
extensions=extensions,
|
extensions=extensions,
|
||||||
http_log_debug=http_log_debug,
|
http_log_debug=http_log_debug,
|
||||||
region_name=instance._region_name,
|
region_name=instance.region_name,
|
||||||
**kwargs
|
**kwargs
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -17,6 +17,7 @@ testtools>=1.4.0 # MIT
|
|||||||
tempest>=12.1.0 # Apache-2.0
|
tempest>=12.1.0 # Apache-2.0
|
||||||
osprofiler>=1.3.0 # Apache-2.0
|
osprofiler>=1.3.0 # Apache-2.0
|
||||||
bandit>=1.0.1 # Apache-2.0
|
bandit>=1.0.1 # Apache-2.0
|
||||||
|
wrapt>=1.7.0 # BSD License
|
||||||
|
|
||||||
# Install these to generate sphinx autodocs
|
# Install these to generate sphinx autodocs
|
||||||
aodhclient>=0.5.0 # Apache-2.0
|
aodhclient>=0.5.0 # Apache-2.0
|
||||||
|
Loading…
Reference in New Issue
Block a user