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:
		| @@ -86,7 +86,7 @@ def get_plugin_modules(group): | ||||
|  | ||||
|         # Add the plugin to the ClientManager | ||||
|         setattr( | ||||
|             ClientManager, | ||||
|             clientmanager.ClientManager, | ||||
|             module.API_NAME, | ||||
|             clientmanager.ClientCache( | ||||
|                 getattr(sys.modules[ep.module_name], 'make_client', None) | ||||
|   | ||||
| @@ -64,7 +64,7 @@ def make_client(instance): | ||||
|                   if ext.name == "list_extensions"] | ||||
|  | ||||
|     # 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( | ||||
|         version, | ||||
| @@ -72,7 +72,7 @@ def make_client(instance): | ||||
|         extensions=extensions, | ||||
|         http_log_debug=http_log_debug, | ||||
|         timings=instance.timing, | ||||
|         region_name=instance._region_name, | ||||
|         region_name=instance.region_name, | ||||
|         **kwargs | ||||
|     ) | ||||
|  | ||||
|   | ||||
| @@ -16,7 +16,6 @@ | ||||
| import logging | ||||
|  | ||||
| from keystoneclient.v2_0 import client as identity_client_v2 | ||||
| from osc_lib.api import auth | ||||
| from osc_lib import utils | ||||
|  | ||||
| from openstackclient.i18n import _ | ||||
| @@ -50,11 +49,11 @@ def make_client(instance): | ||||
|     LOG.debug('Instantiating identity client: %s', identity_client) | ||||
|  | ||||
|     # 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( | ||||
|         session=instance.session, | ||||
|         region_name=instance._region_name, | ||||
|         region_name=instance.region_name, | ||||
|         **kwargs | ||||
|     ) | ||||
|  | ||||
| @@ -70,7 +69,7 @@ def build_option_parser(parser): | ||||
|         help=_('Identity API version, default=%s ' | ||||
|                '(Env: OS_IDENTITY_API_VERSION)') % DEFAULT_API_VERSION, | ||||
|     ) | ||||
|     return auth.build_auth_plugins_option_parser(parser) | ||||
|     return parser | ||||
|  | ||||
|  | ||||
| class IdentityClientv2(identity_client_v2.Client): | ||||
|   | ||||
| @@ -47,15 +47,15 @@ def make_client(instance): | ||||
|  | ||||
|     endpoint = instance.get_endpoint_for_service_type( | ||||
|         API_NAME, | ||||
|         region_name=instance._region_name, | ||||
|         interface=instance._interface, | ||||
|         region_name=instance.region_name, | ||||
|         interface=instance.interface, | ||||
|     ) | ||||
|  | ||||
|     client = image_client( | ||||
|         endpoint, | ||||
|         token=instance.auth.get_token(instance.session), | ||||
|         cacert=instance._cacert, | ||||
|         insecure=instance._insecure, | ||||
|         cacert=instance.cacert, | ||||
|         insecure=not instance.verify, | ||||
|     ) | ||||
|  | ||||
|     # Create the low-level API | ||||
| @@ -70,8 +70,8 @@ def make_client(instance): | ||||
|         session=instance.session, | ||||
|         endpoint=instance.get_endpoint_for_service_type( | ||||
|             IMAGE_API_TYPE, | ||||
|             region_name=instance._region_name, | ||||
|             interface=instance._interface, | ||||
|             region_name=instance.region_name, | ||||
|             interface=instance.interface, | ||||
|         ) | ||||
|     ) | ||||
|  | ||||
|   | ||||
| @@ -34,9 +34,9 @@ API_VERSIONS = { | ||||
| def make_client(instance): | ||||
|     """Returns a network proxy""" | ||||
|     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_interface(API_NAME, instance._interface) | ||||
|     prof.set_interface(API_NAME, instance.interface) | ||||
|     conn = connection.Connection(authenticator=instance.session.auth, | ||||
|                                  verify=instance.session.verify, | ||||
|                                  cert=instance.session.cert, | ||||
|   | ||||
| @@ -32,8 +32,8 @@ def make_client(instance): | ||||
|  | ||||
|     endpoint = instance.get_endpoint_for_service_type( | ||||
|         'object-store', | ||||
|         region_name=instance._region_name, | ||||
|         interface=instance._interface, | ||||
|         region_name=instance.region_name, | ||||
|         interface=instance.interface, | ||||
|     ) | ||||
|  | ||||
|     client = object_store_v1.APIv1( | ||||
|   | ||||
| @@ -16,30 +16,17 @@ | ||||
|  | ||||
| """Command-line interface to the OpenStack APIs""" | ||||
|  | ||||
| import argparse | ||||
| import getpass | ||||
| import locale | ||||
| import logging | ||||
| import six | ||||
| import sys | ||||
| import traceback | ||||
|  | ||||
| from cliff import app | ||||
| from cliff import command | ||||
| 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 osc_lib.api import auth | ||||
| from osc_lib import shell | ||||
| from oslo_utils import importutils | ||||
| from oslo_utils import strutils | ||||
| import six | ||||
|  | ||||
| import openstackclient | ||||
| from openstackclient.common import clientmanager | ||||
| from openstackclient.common import commandmanager | ||||
| from openstackclient.i18n import _ | ||||
|  | ||||
| osprofiler_profiler = importutils.try_import("osprofiler.profiler") | ||||
|  | ||||
| @@ -47,47 +34,9 @@ osprofiler_profiler = importutils.try_import("osprofiler.profiler") | ||||
| DEFAULT_DOMAIN = 'default' | ||||
|  | ||||
|  | ||||
| def prompt_for_password(prompt=None): | ||||
|     """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 = [] | ||||
| class OpenStackShell(shell.OpenStackShell): | ||||
|  | ||||
|     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__( | ||||
|             description=__doc__.strip(), | ||||
| @@ -97,281 +46,28 @@ class OpenStackShell(app.App): | ||||
|  | ||||
|         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 | ||||
|         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): | ||||
|         parser = super(OpenStackShell, self).build_option_parser( | ||||
|             description, | ||||
|             version) | ||||
|         parser = clientmanager.build_plugin_option_parser(parser) | ||||
|         parser = auth.build_auth_plugins_option_parser(parser) | ||||
|         return parser | ||||
|  | ||||
|         # service token auth argument | ||||
|         parser.add_argument( | ||||
|             '--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"), | ||||
|         ) | ||||
|     def _final_defaults(self): | ||||
|         super(OpenStackShell, self)._final_defaults() | ||||
|  | ||||
|         # osprofiler HMAC key argument | ||||
|         if osprofiler_profiler: | ||||
|             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, | ||||
|             ) | ||||
|         # Set default auth type to password | ||||
|         self._auth_type = 'password' | ||||
|  | ||||
|         return clientmanager.build_plugin_option_parser(parser) | ||||
|     def _load_plugins(self): | ||||
|         """Load plugins via stevedore | ||||
|  | ||||
|     def initialize_app(self, argv): | ||||
|         """Global app init bits: | ||||
|  | ||||
|         * set up API versions | ||||
|         * validate authentication info | ||||
|         * authenticate against Identity if requested | ||||
|         osc-lib has no opinion on what plugins should be loaded | ||||
|         """ | ||||
|  | ||||
|         # 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 | ||||
|         for mod in clientmanager.PLUGIN_MODULES: | ||||
|             default_version = getattr(mod, 'DEFAULT_API_VERSION', None) | ||||
| @@ -406,6 +102,11 @@ class OpenStackShell(app.App): | ||||
|                     {'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 | ||||
|         self.command_manager.add_command_group( | ||||
|             'openstack.common') | ||||
| @@ -422,59 +123,19 @@ class OpenStackShell(app.App): | ||||
|         # } | ||||
|         self.command_manager.add_command_group( | ||||
|             'openstack.extension') | ||||
|         # call InitializeXxx() here | ||||
|         # set up additional clients to stuff in to client_manager?? | ||||
|  | ||||
|         # Handle deferred help and exit | ||||
|         self.print_help_if_requested() | ||||
|     def initialize_app(self, argv): | ||||
|         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( | ||||
|             cli_options=self.cloud, | ||||
|             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): | ||||
|     if argv is None: | ||||
|   | ||||
| @@ -13,14 +13,15 @@ | ||||
| #   under the License. | ||||
| # | ||||
|  | ||||
| import copy | ||||
| import fixtures | ||||
| import mock | ||||
| 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.tests import utils | ||||
|  | ||||
|  | ||||
| DEFAULT_AUTH_URL = "http://127.0.0.1:5000/v2.0/" | ||||
| @@ -116,155 +117,50 @@ global_options = { | ||||
|     '--os-interface': (DEFAULT_INTERFACE, True, True) | ||||
| } | ||||
|  | ||||
| auth_options = { | ||||
|     '--os-auth-url': (DEFAULT_AUTH_URL, True, True), | ||||
|     '--os-project-id': (DEFAULT_PROJECT_ID, True, True), | ||||
|     '--os-project-name': (DEFAULT_PROJECT_NAME, True, True), | ||||
|     '--os-domain-id': (DEFAULT_DOMAIN_ID, True, True), | ||||
|     '--os-domain-name': (DEFAULT_DOMAIN_NAME, True, True), | ||||
|     '--os-user-domain-id': (DEFAULT_USER_DOMAIN_ID, True, True), | ||||
|     '--os-user-domain-name': (DEFAULT_USER_DOMAIN_NAME, True, True), | ||||
|     '--os-project-domain-id': (DEFAULT_PROJECT_DOMAIN_ID, True, True), | ||||
|     '--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), | ||||
| } | ||||
|  | ||||
| # Wrap the osc_lib make_shell() function to set the shell class since | ||||
| # osc-lib's TestShell class doesn't allow us to specify it yet. | ||||
| # TODO(dtroyer): remove this once the shell_class_patch patch is released | ||||
| #                in osc-lib | ||||
| def make_shell_wrapper(func, inst, args, kwargs): | ||||
|     if 'shell_class' not in kwargs: | ||||
|         kwargs['shell_class'] = shell.OpenStackShell | ||||
|     return func(*args, **kwargs) | ||||
|  | ||||
|  | ||||
| def opt2attr(opt): | ||||
|     if opt.startswith('--os-'): | ||||
|         attr = opt[5:] | ||||
|     elif opt.startswith('--'): | ||||
|         attr = opt[2:] | ||||
|     else: | ||||
|         attr = opt | ||||
|     return attr.lower().replace('-', '_') | ||||
| wrapt.wrap_function_wrapper( | ||||
|     osc_lib_test_utils, | ||||
|     'make_shell', | ||||
|     make_shell_wrapper, | ||||
| ) | ||||
|  | ||||
|  | ||||
| def opt2env(opt): | ||||
|     return opt[2:].upper().replace('-', '_') | ||||
| class TestShell(osc_lib_test_utils.TestShell): | ||||
|  | ||||
|     # Full name of the OpenStackShell class to test (cliff.app.App subclass) | ||||
|     shell_class_name = "openstackclient.shell.OpenStackShell" | ||||
|  | ||||
| def make_shell(): | ||||
|     """Create a new command shell and mock out some bits.""" | ||||
|     _shell = shell.OpenStackShell() | ||||
|     _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): | ||||
|     # TODO(dtroyer): remove this once the shell_class_patch patch is released | ||||
|     #                in osc-lib | ||||
|     app_patch = shell_class_name | ||||
|  | ||||
|     def setUp(self): | ||||
|         super(TestShell, self).setUp() | ||||
|         patch = "openstackclient.shell.OpenStackShell.run_subcommand" | ||||
|         self.cmd_patch = mock.patch(patch) | ||||
|         self.cmd_save = self.cmd_patch.start() | ||||
|         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" | ||||
|             ) | ||||
|         # TODO(dtroyer): remove this once the shell_class_patch patch is | ||||
|         #                released in osc-lib | ||||
|         self.shell_class = importutils.import_class(self.shell_class_name) | ||||
|  | ||||
|     def _assert_token_endpoint_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) | ||||
|         with mock.patch( | ||||
|                 self.shell_class_name + ".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( | ||||
| @@ -278,11 +174,40 @@ class TestShell(utils.TestCase): | ||||
|                 "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): | ||||
|         with mock.patch("openstackclient.shell.OpenStackShell.initialize_app", | ||||
|                         self.app): | ||||
|             _shell, _cmd = make_shell(), cmd_options + " list server" | ||||
|             fake_execute(_shell, _cmd) | ||||
|         with mock.patch( | ||||
|                 self.shell_class_name + ".initialize_app", | ||||
|                 self.app, | ||||
|         ): | ||||
|             _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.assertEqual(default_args["compute_api_version"], | ||||
| @@ -297,39 +222,17 @@ class TestShell(utils.TestCase): | ||||
|                              _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): | ||||
|  | ||||
|     def setUp(self): | ||||
|         super(TestShellOptions, self).setUp() | ||||
|         self.useFixture(EnvFixture()) | ||||
|         self.useFixture(osc_lib_test_utils.EnvFixture()) | ||||
|  | ||||
|     def _test_options_init_app(self, test_opts): | ||||
|         for opt in test_opts.keys(): | ||||
|             if not test_opts[opt][1]: | ||||
|                 continue | ||||
|             key = opt2attr(opt) | ||||
|             key = osc_lib_test_utils.opt2attr(opt) | ||||
|             if isinstance(test_opts[opt][0], str): | ||||
|                 cmd = opt + " " + test_opts[opt][0] | ||||
|             else: | ||||
| @@ -343,7 +246,7 @@ class TestShellOptions(TestShell): | ||||
|         for opt in test_opts.keys(): | ||||
|             if not test_opts[opt][1]: | ||||
|                 continue | ||||
|             key = opt2attr(opt) | ||||
|             key = osc_lib_test_utils.opt2attr(opt) | ||||
|             if isinstance(test_opts[opt][0], str): | ||||
|                 cmd = opt + " " + test_opts[opt][0] | ||||
|             else: | ||||
| @@ -357,12 +260,12 @@ class TestShellOptions(TestShell): | ||||
|         for opt in test_opts.keys(): | ||||
|             if not test_opts[opt][2]: | ||||
|                 continue | ||||
|             key = opt2attr(opt) | ||||
|             key = osc_lib_test_utils.opt2attr(opt) | ||||
|             kwargs = { | ||||
|                 key: test_opts[opt][0], | ||||
|             } | ||||
|             env = { | ||||
|                 opt2env(opt): test_opts[opt][0], | ||||
|                 osc_lib_test_utils.opt2env(opt): test_opts[opt][0], | ||||
|             } | ||||
|             os.environ = env.copy() | ||||
|             self._assert_initialize_app_arg("", kwargs) | ||||
| @@ -371,37 +274,16 @@ class TestShellOptions(TestShell): | ||||
|         for opt in test_opts.keys(): | ||||
|             if not test_opts[opt][2]: | ||||
|                 continue | ||||
|             key = opt2attr(opt) | ||||
|             key = osc_lib_test_utils.opt2attr(opt) | ||||
|             kwargs = { | ||||
|                 key: test_opts[opt][0], | ||||
|             } | ||||
|             env = { | ||||
|                 opt2env(opt): test_opts[opt][0], | ||||
|                 osc_lib_test_utils.opt2env(opt): test_opts[opt][0], | ||||
|             } | ||||
|             os.environ = env.copy() | ||||
|             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): | ||||
|  | ||||
| @@ -411,7 +293,7 @@ class TestShellTokenAuthEnv(TestShell): | ||||
|             "OS_TOKEN": DEFAULT_TOKEN, | ||||
|             "OS_AUTH_URL": DEFAULT_AUTH_URL, | ||||
|         } | ||||
|         self.useFixture(EnvFixture(env.copy())) | ||||
|         self.useFixture(osc_lib_test_utils.EnvFixture(env.copy())) | ||||
|  | ||||
|     def test_env(self): | ||||
|         flag = "" | ||||
| @@ -455,7 +337,7 @@ class TestShellTokenEndpointAuthEnv(TestShell): | ||||
|             "OS_TOKEN": DEFAULT_TOKEN, | ||||
|             "OS_URL": DEFAULT_SERVICE_URL, | ||||
|         } | ||||
|         self.useFixture(EnvFixture(env.copy())) | ||||
|         self.useFixture(osc_lib_test_utils.EnvFixture(env.copy())) | ||||
|  | ||||
|     def test_env(self): | ||||
|         flag = "" | ||||
| @@ -463,7 +345,7 @@ class TestShellTokenEndpointAuthEnv(TestShell): | ||||
|             "token": DEFAULT_TOKEN, | ||||
|             "url": DEFAULT_SERVICE_URL, | ||||
|         } | ||||
|         self._assert_token_auth(flag, kwargs) | ||||
|         self._assert_token_endpoint_auth(flag, kwargs) | ||||
|  | ||||
|     def test_only_token(self): | ||||
|         flag = "--os-token xyzpdq" | ||||
| @@ -502,85 +384,7 @@ class TestShellCli(TestShell): | ||||
|             "OS_VOLUME_API_VERSION": DEFAULT_VOLUME_API_VERSION, | ||||
|             "OS_NETWORK_API_VERSION": DEFAULT_NETWORK_API_VERSION, | ||||
|         } | ||||
|         self.useFixture(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) | ||||
|         self.useFixture(osc_lib_test_utils.EnvFixture(env.copy())) | ||||
|  | ||||
|     def test_default_env(self): | ||||
|         flag = "" | ||||
| @@ -605,220 +409,34 @@ class TestShellCli(TestShell): | ||||
|         } | ||||
|         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( | ||||
|             _shell, | ||||
|             "--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): | ||||
| class TestShellArgV(TestShell): | ||||
|     """Test the deferred help flag""" | ||||
|  | ||||
|     def setUp(self): | ||||
|         super(TestShellCliEnv, self).setUp() | ||||
|         env = { | ||||
|             'OS_REGION_NAME': 'occ-env', | ||||
|         } | ||||
|         self.useFixture(EnvFixture(env.copy())) | ||||
|         super(TestShellArgV, self).setUp() | ||||
|  | ||||
|     @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_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() | ||||
|     def test_shell_argv(self): | ||||
|         """Test argv decoding | ||||
|  | ||||
|         # Test env var | ||||
|         fake_execute( | ||||
|             _shell, | ||||
|             "--os-cloud megacloud list user", | ||||
|         ) | ||||
|         self.assertEqual( | ||||
|             'megacloud', | ||||
|             _shell.cloud.name, | ||||
|         ) | ||||
|         Python 2 does nothing with argv while Python 3 decodes it into | ||||
|         Unicode before we ever see it.  We manually decode when running | ||||
|         under Python 2 so verify that we get the right argv types. | ||||
|  | ||||
|         # These come from clouds-public.yaml | ||||
|         self.assertEqual( | ||||
|             DEFAULT_AUTH_URL, | ||||
|             _shell.cloud.config['auth']['auth_url'], | ||||
|         ) | ||||
|         self.assertEqual( | ||||
|             'cake', | ||||
|             _shell.cloud.config['donut'], | ||||
|         ) | ||||
|         Use the argv supplied by the test runner so we get actual Python | ||||
|         runtime behaviour; we only need to check the type of argv[0] | ||||
|         which will alwyas be present. | ||||
|         """ | ||||
|  | ||||
|         # 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-env', | ||||
|             _shell.cloud.config['region_name'], | ||||
|         ) | ||||
|         with mock.patch( | ||||
|                 self.shell_class_name + ".run", | ||||
|                 self.app, | ||||
|         ): | ||||
|             # Ensure type gets through unmolested through shell.main() | ||||
|             argv = sys.argv | ||||
|             shell.main(sys.argv) | ||||
|             self.assertEqual(type(argv[0]), type(self.app.call_args[0][0][0])) | ||||
|  | ||||
|     @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_2(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'], | ||||
|         ) | ||||
|  | ||||
|         # These come from the command line | ||||
|         self.assertEqual( | ||||
|             'krikkit', | ||||
|             _shell.cloud.config['region_name'], | ||||
|         ) | ||||
|             # When shell.main() gets sys.argv itself it should be decoded | ||||
|             shell.main() | ||||
|             self.assertEqual(type(u'x'), type(self.app.call_args[0][0][0])) | ||||
|   | ||||
| @@ -57,13 +57,13 @@ def make_client(instance): | ||||
|     extensions = [extension.Extension('list_extensions', list_extensions)] | ||||
|  | ||||
|     # 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( | ||||
|         session=instance.session, | ||||
|         extensions=extensions, | ||||
|         http_log_debug=http_log_debug, | ||||
|         region_name=instance._region_name, | ||||
|         region_name=instance.region_name, | ||||
|         **kwargs | ||||
|     ) | ||||
|  | ||||
|   | ||||
| @@ -17,6 +17,7 @@ testtools>=1.4.0 # MIT | ||||
| tempest>=12.1.0 # Apache-2.0 | ||||
| osprofiler>=1.3.0 # Apache-2.0 | ||||
| bandit>=1.0.1 # Apache-2.0 | ||||
| wrapt>=1.7.0 # BSD License | ||||
|  | ||||
| # Install these to generate sphinx autodocs | ||||
| aodhclient>=0.5.0 # Apache-2.0 | ||||
|   | ||||
		Reference in New Issue
	
	Block a user
	 Dean Troyer
					Dean Troyer