diff options
author | Jenkins <jenkins@review.openstack.org> | 2015-10-20 06:35:10 +0000 |
---|---|---|
committer | Gerrit Code Review <review@openstack.org> | 2015-10-20 06:35:10 +0000 |
commit | d4d1a74c011b8cedaffbb8bccfb83ab19e42d482 (patch) | |
tree | 7c21cf81118f00b57871ae053f2103848a63b53d | |
parent | 89bd4417b948da179779332e41a4fdddfd5dad7d (diff) | |
parent | 4cb9281d5dbd7573350655ba60d15cf6a6312f29 (diff) | |
download | python-ceilometerclient-d4d1a74c011b8cedaffbb8bccfb83ab19e42d482.tar.gz |
Merge "Move to keystone session object"
-rw-r--r-- | ceilometerclient/client.py | 109 | ||||
-rw-r--r-- | ceilometerclient/tests/unit/test_client.py | 27 | ||||
-rw-r--r-- | ceilometerclient/tests/unit/test_shell.py | 4 | ||||
-rw-r--r-- | ceilometerclient/v1/client.py | 48 | ||||
-rw-r--r-- | ceilometerclient/v2/client.py | 107 |
5 files changed, 208 insertions, 87 deletions
diff --git a/ceilometerclient/client.py b/ceilometerclient/client.py index 02194be..3730978 100644 --- a/ceilometerclient/client.py +++ b/ceilometerclient/client.py @@ -10,6 +10,10 @@ # License for the specific language governing permissions and limitations # under the License. +import contextlib +import time + +from keystoneclient import adapter from keystoneclient.auth.identity import v2 as v2_auth from keystoneclient.auth.identity import v3 as v3_auth from keystoneclient import discover @@ -21,6 +25,7 @@ import six.moves.urllib.parse as urlparse from ceilometerclient.common import utils from ceilometerclient import exc from ceilometerclient.openstack.common.apiclient import auth +from ceilometerclient.openstack.common.apiclient import client from ceilometerclient.openstack.common.apiclient import exceptions @@ -365,6 +370,23 @@ def get_client(version, **kwargs): :param api_version: the API version to use ('1' or '2') :param kwargs: keyword args containing credentials, either: + * session: a keystoneauth/keystoneclient session object + * 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 + (Default: public) + * region_name: The default region_name for URL discovery + * endpoint_override: Always use this endpoint URL for requests + for this ceiloclient + * auth: An auth plugin to use instead of the session one + * user_agent: The User-Agent string to set + (Default is python-ceilometer-client) + * connect_retries: the maximum number of retries that should be + attempted for connection errors + * logger: A logging object + + or (DEPRECATED): + * os_auth_token: (DEPRECATED) pre-existing token to re-use, use os_token instead * os_token: pre-existing token to re-use @@ -372,7 +394,7 @@ def get_client(version, **kwargs): use os_endpoint instead * os_endpoint: Ceilometer API endpoint - or: + or (DEPRECATED): * os_username: name of user * os_password: user's password @@ -415,3 +437,88 @@ def get_auth_plugin(endpoint, **kwargs): project_domain_id=kwargs.get('project_domain_id') ) return auth_plugin + + +LEGACY_OPTS = ('auth_plugin', 'auth_url', 'token', 'insecure', 'cacert', + 'tenant_id', 'project_id', 'username', 'password', + 'project_name', 'tenant_name', + 'user_domain_name', 'user_domain_id', + 'project_domain_name', 'project_domain_id', + 'key_file', 'cert_file', 'verify', 'timeout', 'cert') + + +def _construct_http_client(**kwargs): + kwargs = kwargs.copy() + if kwargs.get('session') is not None: + # Drop legacy options + for opt in LEGACY_OPTS: + kwargs.pop(opt, None) + + return SessionClient( + session=kwargs.pop('session'), + service_type=kwargs.pop('service_type', 'metering'), + interface=(kwargs.pop('interface', None) or + kwargs.pop('endpoint_type', 'publicURL')), + region_name=kwargs.pop('region_name', None), + user_agent=kwargs.pop('user_agent', 'python-ceilometerclient'), + auth=kwargs.get('auth', None), + timings=kwargs.pop('timings', None), + **kwargs) + else: + return client.BaseClient(client.HTTPClient( + auth_plugin=kwargs.get('auth_plugin'), + region_name=kwargs.get('region_name'), + endpoint_type=kwargs.get('endpoint_type'), + original_ip=kwargs.get('original_ip'), + verify=kwargs.get('verify'), + cert=kwargs.get('cert'), + timeout=kwargs.get('timeout'), + timings=kwargs.get('timings'), + keyring_saver=kwargs.get('keyring_saver'), + debug=kwargs.get('debug'), + user_agent=kwargs.get('user_agent'), + http=kwargs.get('http') + )) + + +@contextlib.contextmanager +def record_time(times, enabled, *args): + """Record the time of a specific action. + + :param times: A list of tuples holds time data. + :type times: list + :param enabled: Whether timing is enabled. + :type enabled: bool + :param *args: Other data to be stored besides time data, these args + will be joined to a string. + """ + if not enabled: + yield + else: + start = time.time() + yield + end = time.time() + times.append((' '.join(args), start, end)) + + +class SessionClient(adapter.LegacyJsonAdapter): + def __init__(self, *args, **kwargs): + self.times = [] + self.timings = kwargs.pop('timings', False) + super(SessionClient, self).__init__(*args, **kwargs) + + def request(self, url, method, **kwargs): + self.session.request(url, method) + kwargs.setdefault('headers', kwargs.get('headers', {})) + # NOTE(sileht): The standard call raises errors from + # keystoneauth, where we need to raise the gnocchiclient errors. + raise_exc = kwargs.pop('raise_exc', True) + with record_time(self.times, self.timings, method, url): + resp, body = super(SessionClient, self).request(url, + method, + raise_exc=False, + **kwargs) + + if raise_exc and resp.status_code >= 400: + raise exc.from_response(resp, body) + return resp diff --git a/ceilometerclient/tests/unit/test_client.py b/ceilometerclient/tests/unit/test_client.py index 085e259..5498853 100644 --- a/ceilometerclient/tests/unit/test_client.py +++ b/ceilometerclient/tests/unit/test_client.py @@ -54,6 +54,26 @@ class ClientTest(utils.BaseTestCase): ks_exc.EndpointNotFound return client.get_client(api_version, **env) + def test_client_v2_with_session(self): + resp = mock.Mock(status_code=200, text=b'') + resp.json.return_value = [] + session = mock.Mock() + session.request.return_value = resp + c = client.get_client(2, session=session) + c.resources.list() + self.assertTrue(session.request.called) + self.assertTrue(resp.json.called) + + def test_client_v1_with_session(self): + resp = mock.Mock(status_code=200, text=b'') + resp.json.return_value = {'resources': []} + session = mock.Mock() + session.request.return_value = resp + c = client.get_client(1, session=session) + c.resources.list() + self.assertTrue(session.request.called) + self.assertTrue(resp.json.called) + def test_client_version(self): c1 = self.create_client(env=FAKE_ENV, api_version=1) self.assertIsInstance(c1, v1client.Client) @@ -144,7 +164,7 @@ class ClientTest(utils.BaseTestCase): self._test_v2_client_timeout_integer(30, 30) @mock.patch.object(ks_session, 'Session') - def test_v2_client_timeout_keystone_seesion(self, mocked_session): + def test_v2_client_timeout_keystone_session(self, mocked_session): mocked_session.side_effect = RuntimeError('Stop!') env = FAKE_ENV.copy() env['timeout'] = 5 @@ -159,7 +179,8 @@ class ClientTest(utils.BaseTestCase): env = FAKE_ENV.copy() env['cacert'] = '/path/to/cacert' client = self.create_client(env) - self.assertEqual('/path/to/cacert', client.client.verify) + self.assertEqual('/path/to/cacert', + client.http_client.http_client.verify) def test_v2_client_certfile_and_keyfile(self): env = FAKE_ENV.copy() @@ -167,7 +188,7 @@ class ClientTest(utils.BaseTestCase): env['key_file'] = '/path/to/keycert' client = self.create_client(env) self.assertEqual(('/path/to/cert', '/path/to/keycert'), - client.client.cert) + client.http_client.http_client.cert) def test_v2_client_insecure(self): env = FAKE_ENV.copy() diff --git a/ceilometerclient/tests/unit/test_shell.py b/ceilometerclient/tests/unit/test_shell.py index d9bbcff..6f29023 100644 --- a/ceilometerclient/tests/unit/test_shell.py +++ b/ceilometerclient/tests/unit/test_shell.py @@ -19,11 +19,11 @@ import mock import six from testtools import matchers +from ceilometerclient import client from ceilometerclient import exc from ceilometerclient.openstack.common.apiclient import client as api_client from ceilometerclient import shell as ceilometer_shell from ceilometerclient.tests.unit import utils -from ceilometerclient.v2 import client as v2client FAKE_V2_ENV = {'OS_USERNAME': 'username', 'OS_PASSWORD': 'password', @@ -41,7 +41,7 @@ class ShellTestBase(utils.BaseTestCase): @mock.patch('sys.stdout', new=six.StringIO()) @mock.patch.object(ks_session, 'Session', mock.MagicMock()) - @mock.patch.object(v2client.client.HTTPClient, + @mock.patch.object(client.client.HTTPClient, 'client_request', mock.MagicMock()) def shell(self, argstr): try: diff --git a/ceilometerclient/v1/client.py b/ceilometerclient/v1/client.py index 4f531bf..8c382de 100644 --- a/ceilometerclient/v1/client.py +++ b/ceilometerclient/v1/client.py @@ -15,40 +15,40 @@ from ceilometerclient import client as ceiloclient -from ceilometerclient.openstack.common.apiclient import client from ceilometerclient.v1 import meters class Client(object): """Client for the Ceilometer v1 API. - :param string endpoint: A user-supplied endpoint URL for the ceilometer - service. - :param function token: Provides token for authentication. - :param integer timeout: Allows customization of the timeout for client - http requests. (optional) + :param session: a keystoneauth/keystoneclient session object + :type session: keystoneclient.session.Session + :param str service_type: The default service_type for URL discovery + :param str service_name: The default service_name for URL discovery + :param str interface: The default interface for URL discovery + (Default: public) + :param str region_name: The default region_name for URL discovery + :param str endpoint_override: Always use this endpoint URL for requests + :param for this ceiloclient + :param auth: An auth plugin to use instead of the session one + :type auth: keystoneclient.auth.base.BaseAuthPlugin + :param str user_agent: The User-Agent string to set + (Default is python-ceilometer-client) + :param int connect_retries: the maximum number of retries that should be + attempted for connection errors + :param logger: A logging object + :type logger: logging.Logger """ def __init__(self, *args, **kwargs): """Initialize a new client for the Ceilometer v1 API.""" - self.auth_plugin = kwargs.get('auth_plugin') \ - or ceiloclient.get_auth_plugin(*args, **kwargs) - self.client = client.HTTPClient( - auth_plugin=self.auth_plugin, - region_name=kwargs.get('region_name'), - endpoint_type=kwargs.get('endpoint_type'), - original_ip=kwargs.get('original_ip'), - verify=kwargs.get('verify'), - cert=kwargs.get('cert'), - timeout=kwargs.get('timeout'), - timings=kwargs.get('timings'), - keyring_saver=kwargs.get('keyring_saver'), - debug=kwargs.get('debug'), - user_agent=kwargs.get('user_agent'), - http=kwargs.get('http') - ) - - self.http_client = client.BaseClient(self.client) + + if not kwargs.get('auth_plugin'): + kwargs['auth_plugin'] = ceiloclient.get_auth_plugin(*args, + **kwargs) + self.auth_plugin = kwargs.get('auth_plugin') + + self.http_client = ceiloclient._construct_http_client(**kwargs) self.meters = meters.MeterManager(self.http_client) self.samples = meters.SampleManager(self.http_client) self.users = meters.UserManager(self.http_client) diff --git a/ceilometerclient/v2/client.py b/ceilometerclient/v2/client.py index 0ba76e5..16f1c35 100644 --- a/ceilometerclient/v2/client.py +++ b/ceilometerclient/v2/client.py @@ -17,7 +17,6 @@ import copy from ceilometerclient import client as ceiloclient -from ceilometerclient.openstack.common.apiclient import client from ceilometerclient.v2 import alarms from ceilometerclient.v2 import capabilities from ceilometerclient.v2 import event_types @@ -35,36 +34,34 @@ from keystoneclient import exceptions class Client(object): """Client for the Ceilometer v2 API. - :param endpoint: A user-supplied endpoint URL for the ceilometer - service. - :type endpoint: string - :param token: Provides token for authentication. - :type token: function - :param timeout: Allows customization of the timeout for client - http requests. (optional) - :type timeout: integer + :param session: a keystoneauth/keystoneclient session object + :type session: keystoneclient.session.Session + :param str service_type: The default service_type for URL discovery + :param str service_name: The default service_name for URL discovery + :param str interface: The default interface for URL discovery + (Default: public) + :param str region_name: The default region_name for URL discovery + :param str endpoint_override: Always use this endpoint URL for requests + :param for this ceiloclient + :param auth: An auth plugin to use instead of the session one + :type auth: keystoneclient.auth.base.BaseAuthPlugin + :param str user_agent: The User-Agent string to set + (Default is python-ceilometer-client) + :param int connect_retries: the maximum number of retries that should be + attempted for connection errors + :param logger: A logging object + :type logger: logging.Logger """ def __init__(self, *args, **kwargs): """Initialize a new client for the Ceilometer v2 API.""" - self.auth_plugin = kwargs.get('auth_plugin') \ - or ceiloclient.get_auth_plugin(*args, **kwargs) - self.client = client.HTTPClient( - auth_plugin=self.auth_plugin, - region_name=kwargs.get('region_name'), - endpoint_type=kwargs.get('endpoint_type'), - original_ip=kwargs.get('original_ip'), - verify=kwargs.get('verify'), - cert=kwargs.get('cert'), - timeout=kwargs.get('timeout'), - timings=kwargs.get('timings'), - keyring_saver=kwargs.get('keyring_saver'), - debug=kwargs.get('debug'), - user_agent=kwargs.get('user_agent'), - http=kwargs.get('http') - ) - self.http_client = client.BaseClient(self.client) + if not kwargs.get('auth_plugin'): + kwargs['auth_plugin'] = ceiloclient.get_auth_plugin(*args, + **kwargs) + self.auth_plugin = kwargs.get('auth_plugin') + + self.http_client = ceiloclient._construct_http_client(**kwargs) self.alarm_client, aodh_enabled = self._get_alarm_client(**kwargs) self.meters = meters.MeterManager(self.http_client) self.samples = samples.OldSampleManager(self.http_client) @@ -87,37 +84,33 @@ class Client(object): def _get_alarm_client(self, **kwargs): """Get client for alarm manager that redirect to aodh.""" - - self.alarm_auth_plugin = copy.deepcopy(self.auth_plugin) + kwargs = copy.deepcopy(kwargs) + self.alarm_auth_plugin = kwargs.get('auth_plugin') aodh_endpoint = kwargs.get('aodh_endpoint') - if aodh_endpoint: - self.alarm_auth_plugin.opts['endpoint'] = aodh_endpoint - elif not kwargs.get('auth_url'): - # Users may just provided ceilometer endpoint and token, and no - # auth_url, in this case, we need 'aodh_endpoint' also provided, - # otherwise we cannot get aodh endpoint from keystone, and assume - # aodh is unavailable. - return self.http_client, False + if kwargs.get('session') is not None: + if aodh_endpoint: + kwargs['endpoint_override'] = aodh_endpoint + else: + kwargs["service_type"] = "alarming" + try: + return ceiloclient._construct_http_client(**kwargs), True + except exceptions.EndpointNotFound: + return self.http_client, False else: - try: - # NOTE(liusheng): Getting the aodh's endpoint to rewrite - # the endpoint of alarm auth_plugin. - self.alarm_auth_plugin.redirect_to_aodh_endpoint( - kwargs.get('timeout')) - except exceptions.EndpointNotFound: + if aodh_endpoint: + kwargs["auth_plugin"].opts['endpoint'] = aodh_endpoint + elif not kwargs.get('auth_url'): + # Users may just provided ceilometer endpoint and token, and no + # auth_url, in this case, we need 'aodh_endpoint' also + # provided, otherwise we cannot get aodh endpoint from + # keystone, and assume aodh is unavailable. return self.http_client, False - alarm_client = client.HTTPClient( - auth_plugin=self.alarm_auth_plugin, - region_name=kwargs.get('region_name'), - endpoint_type=kwargs.get('endpoint_type'), - original_ip=kwargs.get('original_ip'), - verify=kwargs.get('verify'), - cert=kwargs.get('cert'), - timeout=kwargs.get('timeout'), - timings=kwargs.get('timings'), - keyring_saver=kwargs.get('keyring_saver'), - debug=kwargs.get('debug'), - user_agent=kwargs.get('user_agent'), - http=kwargs.get('http') - ) - return client.BaseClient(alarm_client), True + else: + try: + # NOTE(liusheng): Getting the aodh's endpoint to rewrite + # the endpoint of alarm auth_plugin. + kwargs["auth_plugin"].redirect_to_aodh_endpoint( + kwargs.get('timeout')) + except exceptions.EndpointNotFound: + return self.http_client, False + return ceiloclient._construct_http_client(**kwargs), True |