From 1fd43be5cea7953d67b0a4b60497e36c2fbdb5ad Mon Sep 17 00:00:00 2001 From: Eric Fried Date: Wed, 26 Apr 2017 17:38:34 -0500 Subject: [PATCH] Introduce keystoneauth1.loading.adapter In the spirit of keystoneauth1.loading.session, keystoneauth1.loading.adapter.Adapter is a BaseLoader subclass providing oslo_config* options suitable for inclusion by config groups wishing to support keystoneauth1.adapter.Adapter operations such as endpoint discovery. *Future work should be done to move the argparse options from keystoneauth1.adapter.Adapter into the new loading.adapter.Adapter class for consistency. Change-Id: Icb8c295799cc5ef4ca16f44c238838668fb7ab93 Partial-Implements: bp use-service-catalog-for-endpoints --- keystoneauth1/adapter.py | 6 + keystoneauth1/loading/__init__.py | 15 ++ keystoneauth1/loading/adapter.py | 131 ++++++++++++++++++ .../tests/unit/loading/test_adapter.py | 51 +++++++ keystoneauth1/tests/unit/loading/test_cli.py | 7 +- 5 files changed, 206 insertions(+), 4 deletions(-) create mode 100644 keystoneauth1/loading/adapter.py create mode 100644 keystoneauth1/tests/unit/loading/test_adapter.py diff --git a/keystoneauth1/adapter.py b/keystoneauth1/adapter.py index 57216961..460c8809 100644 --- a/keystoneauth1/adapter.py +++ b/keystoneauth1/adapter.py @@ -237,6 +237,7 @@ class Adapter(object): def delete(self, url, **kwargs): return self.request(url, 'DELETE', **kwargs) + # TODO(efried): Move this to loading.adapter.Adapter @classmethod def register_argparse_arguments(cls, parser, service_type=None): """Attach arguments to a given argparse Parser for Adapters. @@ -286,6 +287,7 @@ class Adapter(object): default=os.environ.get('OS_API_VERSION', None), help='Which version of the service API to use') + # TODO(efried): Move this to loading.adapter.Adapter @classmethod def register_service_argparse_arguments(cls, parser, service_type): """Attach arguments to a given argparse Parser for Adapters. @@ -387,9 +389,13 @@ class LegacyJsonAdapter(Adapter): return resp, body +# TODO(efried): Deprecate this in favor of +# loading.adapter.register_argparse_arguments def register_adapter_argparse_arguments(*args, **kwargs): return Adapter.register_argparse_arguments(*args, **kwargs) +# TODO(efried): Deprecate this in favor of +# loading.adapter.register_service_argparse_arguments def register_service_adapter_argparse_arguments(*args, **kwargs): return Adapter.register_service_argparse_arguments(*args, **kwargs) diff --git a/keystoneauth1/loading/__init__.py b/keystoneauth1/loading/__init__.py index 9b9aaf25..e31b2f42 100644 --- a/keystoneauth1/loading/__init__.py +++ b/keystoneauth1/loading/__init__.py @@ -10,6 +10,7 @@ # License for the specific language governing permissions and limitations # under the License. +from keystoneauth1.loading import adapter from keystoneauth1.loading.base import * # noqa from keystoneauth1.loading import cli from keystoneauth1.loading import conf @@ -32,6 +33,13 @@ register_session_conf_options = session.register_conf_options load_session_from_conf_options = session.load_from_conf_options get_session_conf_options = session.get_conf_options +register_adapter_argparse_arguments = adapter.register_argparse_arguments +register_service_adapter_argparse_arguments = ( + adapter.register_service_argparse_arguments) +register_adapter_conf_options = adapter.register_conf_options +load_adapter_from_conf_options = adapter.load_from_conf_options +get_adapter_conf_options = adapter.get_conf_options + __all__ = ( # loading.base @@ -65,6 +73,13 @@ __all__ = ( 'load_session_from_conf_options', 'get_session_conf_options', + # adapter + 'register_adapter_argparse_arguments', + 'register_service_adapter_argparse_arguments', + 'register_adapter_conf_options', + 'load_adapter_from_conf_options', + 'get_adapter_conf_options', + # loading.opts 'Opt', ) diff --git a/keystoneauth1/loading/adapter.py b/keystoneauth1/loading/adapter.py new file mode 100644 index 00000000..5b3638db --- /dev/null +++ b/keystoneauth1/loading/adapter.py @@ -0,0 +1,131 @@ +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +from keystoneauth1 import adapter +from keystoneauth1.loading import _utils +from keystoneauth1.loading import base + + +__all__ = ('register_argparse_arguments', + 'register_service_argparse_arguments', + 'register_conf_options', + 'load_from_conf_options', + 'get_conf_options') + + +class Adapter(base.BaseLoader): + + @property + def plugin_class(self): + return adapter.Adapter + + def get_options(self): + return [] + + @staticmethod + def get_conf_options(): + """Get oslo_config options that are needed for a :py:class:`.Adapter`. + + These may be useful without being registered for config file generation + or to manipulate the options before registering them yourself. + + The options that are set are: + :service_type: The default service_type for URL discovery. + :service_name: The default service_name for URL discovery. + :interface: The default interface for URL discovery. + :region_name: The default region_name for URL discovery. + :endpoint_override: Always use this endpoint URL for requests + for this client. + + :returns: A list of oslo_config options. + """ + cfg = _utils.get_oslo_config() + + return [cfg.StrOpt('service-type', + help='The default service_type for endpoint URL ' + 'discovery.'), + cfg.StrOpt('service-name', + help='The default service_name for endpoint URL ' + 'discovery.'), + cfg.StrOpt('interface', + help='The default interface for endpoint URL ' + 'discovery.'), + cfg.StrOpt('region-name', + help='The default region_name for endpoint URL ' + 'discovery.'), + cfg.StrOpt('endpoint-override', + help='Always use this endpoint URL for requests ' + 'for this client.'), + ] + + def register_conf_options(self, conf, group): + """Register the oslo_config options that are needed for an Adapter. + + The options that are set are: + :service_type: The default service_type for URL discovery. + :service_name: The default service_name for URL discovery. + :interface: The default interface for URL discovery. + :region_name: The default region_name for URL discovery. + :endpoint_override: Always use this endpoint URL for requests + for this client. + + :param oslo_config.Cfg conf: config object to register with. + :param string group: The ini group to register options in. + :returns: The list of options that was registered. + """ + opts = self.get_conf_options() + conf.register_group(_utils.get_oslo_config().OptGroup(group)) + conf.register_opts(opts, group=group) + return opts + + def load_from_conf_options(self, conf, group, **kwargs): + """Create an Adapter object from an oslo_config object. + + The options must have been previously registered with + register_conf_options. + + :param oslo_config.Cfg conf: config object to register with. + :param string group: The ini group to register options in. + :param dict kwargs: Additional parameters to pass to Adapter + construction. + :returns: A new Adapter object. + :rtype: :py:class:`.Adapter` + """ + c = conf[group] + + kwargs.setdefault('service_type', c.service_type) + kwargs.setdefault('service_name', c.service_name) + kwargs.setdefault('interface', c.interface) + kwargs.setdefault('region_name', c.region_name) + kwargs.setdefault('endpoint_override', c.endpoint_override) + + return self.load_from_options(**kwargs) + + +def register_argparse_arguments(*args, **kwargs): + return adapter.register_adapter_argparse_arguments(*args, **kwargs) + + +def register_service_argparse_arguments(*args, **kwargs): + return adapter.register_service_adapter_argparse_arguments(*args, **kwargs) + + +def register_conf_options(*args, **kwargs): + return Adapter().register_conf_options(*args, **kwargs) + + +def load_from_conf_options(*args, **kwargs): + return Adapter().load_from_conf_options(*args, **kwargs) + + +def get_conf_options(): + return Adapter.get_conf_options() diff --git a/keystoneauth1/tests/unit/loading/test_adapter.py b/keystoneauth1/tests/unit/loading/test_adapter.py new file mode 100644 index 00000000..cf72b2ad --- /dev/null +++ b/keystoneauth1/tests/unit/loading/test_adapter.py @@ -0,0 +1,51 @@ +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +from oslo_config import cfg +from oslo_config import fixture as config + +from keystoneauth1 import loading +from keystoneauth1.tests.unit.loading import utils + + +class ConfLoadingTests(utils.TestCase): + + GROUP = 'adaptergroup' + + def setUp(self): + super(ConfLoadingTests, self).setUp() + + self.conf_fx = self.useFixture(config.Config()) + loading.register_adapter_conf_options(self.conf_fx.conf, self.GROUP) + + def test_load(self): + self.conf_fx.config( + service_type='type', service_name='name', interface='iface', + region_name='region', endpoint_override='endpoint', + group=self.GROUP) + adap = loading.load_adapter_from_conf_options( + self.conf_fx.conf, self.GROUP, session='session', auth='auth') + self.assertEqual('type', adap.service_type) + self.assertEqual('name', adap.service_name) + self.assertEqual('iface', adap.interface) + self.assertEqual('region', adap.region_name) + self.assertEqual('endpoint', adap.endpoint_override) + self.assertEqual('session', adap.session) + self.assertEqual('auth', adap.auth) + + def test_get_conf_options(self): + opts = loading.get_adapter_conf_options() + for opt in opts: + self.assertTrue(isinstance(opt, cfg.StrOpt)) + self.assertEqual({'service-type', 'service-name', 'interface', + 'region-name', 'endpoint-override'}, + {opt.name for opt in opts}) diff --git a/keystoneauth1/tests/unit/loading/test_cli.py b/keystoneauth1/tests/unit/loading/test_cli.py index 8cf95102..5bbc32c9 100644 --- a/keystoneauth1/tests/unit/loading/test_cli.py +++ b/keystoneauth1/tests/unit/loading/test_cli.py @@ -16,7 +16,6 @@ import uuid import fixtures import mock -from keystoneauth1 import adapter from keystoneauth1 import loading from keystoneauth1.loading import cli from keystoneauth1.tests.unit.loading import utils @@ -201,7 +200,7 @@ class CliTests(utils.TestCase): def test_adapter_service_type(self): argv = ['--os-service-type', 'compute'] - adapter.Adapter.register_argparse_arguments(self.p, 'compute') + loading.register_adapter_argparse_arguments(self.p, 'compute') opts = self.p.parse_args(argv) self.assertEqual('compute', opts.os_service_type) @@ -210,8 +209,8 @@ class CliTests(utils.TestCase): def test_adapter_service_type_per_service(self): argv = ['--os-compute-service-type', 'weirdness'] - adapter.Adapter.register_argparse_arguments(self.p, 'compute') - adapter.Adapter.register_service_argparse_arguments(self.p, 'compute') + loading.register_adapter_argparse_arguments(self.p, 'compute') + loading.register_service_adapter_argparse_arguments(self.p, 'compute') opts = self.p.parse_args(argv) self.assertEqual('compute', opts.os_service_type)