From 64b28d42eda595a6fb4ee8b46d93cd61e612aae1 Mon Sep 17 00:00:00 2001 From: Monty Taylor Date: Sun, 2 Apr 2017 11:22:34 -0500 Subject: Add ability to pass in user_agent keystoneauth supports adding a user_agent info to the Session and Adapter via app_name. Allow users to add app_name/app_name and versions as desired. Also, add os-client-config into additional_user_agent. As an example, once this is landed and plumbed through shade, nodepool will set app_name='nodepool' and we'll have: User-Agent: nodepool/0.4.0 os-client-config/1.26.1 shade/1.19.1 keystoneauth1/2.18.0 python-requests/2.13.0 CPython/2.7.12 Change-Id: I1eb4dbd2587dcbe297b5c060c3c34b68ef51ef5e --- os_client_config/__init__.py | 19 +++++++++++++++---- os_client_config/cloud_config.py | 11 ++++++++++- os_client_config/config.py | 5 +++++ os_client_config/tests/base.py | 2 ++ os_client_config/tests/test_cloud_config.py | 18 ++++++++++++++++-- 5 files changed, 48 insertions(+), 7 deletions(-) diff --git a/os_client_config/__init__.py b/os_client_config/__init__.py index a36a130..1f1266c 100644 --- a/os_client_config/__init__.py +++ b/os_client_config/__init__.py @@ -23,9 +23,14 @@ from os_client_config.config import OpenStackConfig # noqa __version__ = pbr.version.VersionInfo('os_client_config').version_string() -def get_config(service_key=None, options=None, **kwargs): +def get_config( + service_key=None, options=None, + app_name=None, app_version=None, + **kwargs): load_yaml_config = kwargs.pop('load_yaml_config', True) - config = OpenStackConfig(load_yaml_config=load_yaml_config) + config = OpenStackConfig( + load_yaml_config=load_yaml_config, + app_name=app_name, app_version=app_version) if options: config.register_argparse_arguments(options, sys.argv, service_key) parsed_options = options.parse_known_args(sys.argv) @@ -35,7 +40,10 @@ def get_config(service_key=None, options=None, **kwargs): return config.get_one_cloud(options=parsed_options, **kwargs) -def make_rest_client(service_key, options=None, **kwargs): +def make_rest_client( + service_key, options=None, + app_name=None, app_version=None, + **kwargs): """Simple wrapper function. It has almost no features. This will get you a raw requests Session Adapter that is mounted @@ -48,7 +56,10 @@ def make_rest_client(service_key, options=None, **kwargs): get_session_client on it. This function is to make it easy to poke at OpenStack REST APIs with a properly configured keystone session. """ - cloud = get_config(service_key=service_key, options=options, **kwargs) + cloud = get_config( + service_key=service_key, options=options, + app_name=app_name, app_version=app_version, + **kwargs) return cloud.get_session_client(service_key) # Backwards compat - simple_client was a terrible name simple_client = make_rest_client diff --git a/os_client_config/cloud_config.py b/os_client_config/cloud_config.py index 45bb825..0860924 100644 --- a/os_client_config/cloud_config.py +++ b/os_client_config/cloud_config.py @@ -21,6 +21,7 @@ import keystoneauth1.exceptions.catalog from keystoneauth1 import session import requestsexceptions +import os_client_config from os_client_config import _log from os_client_config import constructors from os_client_config import exceptions @@ -71,7 +72,8 @@ def _make_key(key, service_type): class CloudConfig(object): def __init__(self, name, region, config, force_ipv4=False, auth_plugin=None, - openstack_config=None, session_constructor=None): + openstack_config=None, session_constructor=None, + app_name=None, app_version=None): self.name = name self.region = region self.config = config @@ -81,6 +83,8 @@ class CloudConfig(object): self._openstack_config = openstack_config self._keystone_session = None self._session_constructor = session_constructor or session.Session + self._app_name = app_name + self._app_version = app_version def __getattr__(self, key): """Return arbitrary attributes.""" @@ -211,9 +215,14 @@ class CloudConfig(object): requestsexceptions.squelch_warnings(insecure_requests=not verify) self._keystone_session = self._session_constructor( auth=self._auth, + app_name=self._app_name, + app_version=self._app_version, verify=verify, cert=cert, timeout=self.config['api_timeout']) + if hasattr(self._keystone_session, 'additional_user_agent'): + self._keystone_session.additional_user_agent.append( + ('os-client-config', os_client_config.__version__)) return self._keystone_session def get_session_client(self, service_key): diff --git a/os_client_config/config.py b/os_client_config/config.py index 64e3a13..89b5c6c 100644 --- a/os_client_config/config.py +++ b/os_client_config/config.py @@ -174,9 +174,12 @@ class OpenStackConfig(object): override_defaults=None, force_ipv4=None, envvar_prefix=None, secure_files=None, pw_func=None, session_constructor=None, + app_name=None, app_version=None, load_yaml_config=True): self.log = _log.setup_logging(__name__) self._session_constructor = session_constructor + self._app_name = app_name + self._app_version = app_version if load_yaml_config: self._config_files = config_files or CONFIG_FILES @@ -1088,6 +1091,8 @@ class OpenStackConfig(object): auth_plugin=auth_plugin, openstack_config=self, session_constructor=self._session_constructor, + app_name=self._app_name, + app_version=self._app_version, ) def get_one_cloud_osc( diff --git a/os_client_config/tests/base.py b/os_client_config/tests/base.py index 85a42c5..9710782 100644 --- a/os_client_config/tests/base.py +++ b/os_client_config/tests/base.py @@ -212,6 +212,8 @@ class TestCase(base.BaseTestCase): self.secure_yaml = _write_yaml(SECURE_CONF) self.vendor_yaml = _write_yaml(VENDOR_CONF) self.no_yaml = _write_yaml(NO_CONF) + self.useFixture(fixtures.MonkeyPatch( + 'os_client_config.__version__', '1.2.3')) # Isolate the test runs from the environment # Do this as two loops because you can't modify the dict in a loop diff --git a/os_client_config/tests/test_cloud_config.py b/os_client_config/tests/test_cloud_config.py index cb6d91c..0671fcc 100644 --- a/os_client_config/tests/test_cloud_config.py +++ b/os_client_config/tests/test_cloud_config.py @@ -179,15 +179,25 @@ class TestCloudConfig(base.TestCase): def test_get_session(self, mock_session): config_dict = defaults.get_defaults() config_dict.update(fake_services_dict) + fake_session = mock.Mock() + fake_session.additional_user_agent = [] + mock_session.return_value = fake_session cc = cloud_config.CloudConfig( "test1", "region-al", config_dict, auth_plugin=mock.Mock()) cc.get_session() mock_session.assert_called_with( auth=mock.ANY, - verify=True, cert=None, timeout=None) + verify=True, cert=None, timeout=None, + app_name=None, app_version=None) + self.assertEqual( + fake_session.additional_user_agent, + [('os-client-config', '1.2.3')]) @mock.patch.object(ksa_session, 'Session') def test_get_session_with_timeout(self, mock_session): + fake_session = mock.Mock() + fake_session.additional_user_agent = [] + mock_session.return_value = fake_session config_dict = defaults.get_defaults() config_dict.update(fake_services_dict) config_dict['api_timeout'] = 9 @@ -196,7 +206,11 @@ class TestCloudConfig(base.TestCase): cc.get_session() mock_session.assert_called_with( auth=mock.ANY, - verify=True, cert=None, timeout=9) + verify=True, cert=None, timeout=9, + app_name=None, app_version=None) + self.assertEqual( + fake_session.additional_user_agent, + [('os-client-config', '1.2.3')]) @mock.patch.object(ksa_session, 'Session') def test_override_session_endpoint_override(self, mock_session): -- cgit v1.2.1