Support getting client with keystone session

This change will allow cloudkitty client to use keystoneclient/
keystoneauth session object.

Change-Id: Icc4bf5da12fb24d189fc38daf1b5cfb4a43228aa
This commit is contained in:
Chaozhe.Chen
2015-12-10 01:08:22 +08:00
parent 6dbfc4502e
commit df4e8360e2
3 changed files with 144 additions and 26 deletions

View File

@@ -10,6 +10,10 @@
# License for the specific language governing permissions and limitations # License for the specific language governing permissions and limitations
# under the License. # 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 v2 as v2_auth
from keystoneclient.auth.identity import v3 as v3_auth from keystoneclient.auth.identity import v3 as v3_auth
from keystoneclient import discover from keystoneclient import discover
@@ -21,6 +25,7 @@ import six.moves.urllib.parse as urlparse
from cloudkittyclient.common import utils from cloudkittyclient.common import utils
from cloudkittyclient import exc from cloudkittyclient import exc
from cloudkittyclient.openstack.common.apiclient import auth from cloudkittyclient.openstack.common.apiclient import auth
from cloudkittyclient.openstack.common.apiclient import client
from cloudkittyclient.openstack.common.apiclient import exceptions from cloudkittyclient.openstack.common.apiclient import exceptions
@@ -250,9 +255,28 @@ def get_client(version, **kwargs):
:param api_version: the API version to use ('1') :param api_version: the API version to use ('1')
:param kwargs: keyword args containing credentials, either: :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 cloudkittyclient
* auth: An auth plugin to use instead of the session one
* user_agent: The User-Agent string to set
(Default is python-cloudkittyclient)
* connect_retries: the maximum number of retries that should be
attempted for connection errors
* logger: A logging object
or (DEPRECATED):
* os_token: pre-existing token to re-use * os_token: pre-existing token to re-use
* os_endpoint: Cloudkitty API endpoint * os_endpoint: Cloudkitty API endpoint
or:
or (DEPRECATED):
* os_username: name of user * os_username: name of user
* os_password: user's password * os_password: user's password
* os_user_id: user's id * os_user_id: user's id
@@ -314,3 +338,87 @@ def get_auth_plugin(endpoint, **kwargs):
project_domain_id=kwargs.get('project_domain_id') project_domain_id=kwargs.get('project_domain_id')
) )
return auth_plugin 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', 'rating') or 'rating',
interface=kwargs.pop('interface', kwargs.pop('endpoint_type',
'publicURL')),
region_name=kwargs.pop('region_name', None),
user_agent=kwargs.pop('user_agent', 'python-cloudkittyclient'),
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):
kwargs.setdefault('headers', kwargs.get('headers', {}))
# NOTE(sileht): The standard call raises errors from
# keystoneauth, where we need to raise the cloudkittyclient 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

View File

@@ -45,6 +45,16 @@ class ClientTest(utils.BaseTestCase):
def setUp(self): def setUp(self):
super(ClientTest, self).setUp() super(ClientTest, self).setUp()
def test_client_v1_with_session(self):
resp = mock.Mock(status_code=200, text=b'')
resp.json.return_value = {"modules": []}
session = mock.Mock()
session.request.return_value = resp
c = client.get_client(1, session=session)
c.modules.list()
self.assertTrue(session.request.called)
self.assertTrue(resp.json.called)
def test_client_version(self): def test_client_version(self):
c1 = self.create_client(env=FAKE_ENV, api_version=1) c1 = self.create_client(env=FAKE_ENV, api_version=1)
self.assertIsInstance(c1, v1client.Client) self.assertIsInstance(c1, v1client.Client)
@@ -137,7 +147,8 @@ class ClientTest(utils.BaseTestCase):
env = FAKE_ENV.copy() env = FAKE_ENV.copy()
env['cacert'] = '/path/to/cacert' env['cacert'] = '/path/to/cacert'
client = self.create_client(env) 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_v1_client_certfile_and_keyfile(self): def test_v1_client_certfile_and_keyfile(self):
env = FAKE_ENV.copy() env = FAKE_ENV.copy()
@@ -145,4 +156,4 @@ class ClientTest(utils.BaseTestCase):
env['key_file'] = '/path/to/keycert' env['key_file'] = '/path/to/keycert'
client = self.create_client(env) client = self.create_client(env)
self.assertEqual(('/path/to/cert', '/path/to/keycert'), self.assertEqual(('/path/to/cert', '/path/to/keycert'),
client.client.cert) client.http_client.http_client.cert)

View File

@@ -16,7 +16,6 @@
from stevedore import extension from stevedore import extension
from cloudkittyclient import client as ckclient from cloudkittyclient import client as ckclient
from cloudkittyclient.openstack.common.apiclient import client
from cloudkittyclient.v1 import collector from cloudkittyclient.v1 import collector
from cloudkittyclient.v1 import core from cloudkittyclient.v1 import core
from cloudkittyclient.v1 import report from cloudkittyclient.v1 import report
@@ -28,33 +27,33 @@ SUBMODULES_NAMESPACE = 'cloudkitty.client.modules'
class Client(object): class Client(object):
"""Client for the Cloudkitty v1 API. """Client for the Cloudkitty v1 API.
:param string endpoint: A user-supplied endpoint URL for the cloudkitty :param session: a keystoneauth/keystoneclient session object
service. :type session: keystoneclient.session.Session
:param function token: Provides token for authentication. :param str service_type: The default service_type for URL discovery
:param integer timeout: Allows customization of the timeout for client :param str service_name: The default service_name for URL discovery
http requests. (optional) :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
for this cloudkittyclient
: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-cloudkittyclient)
: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): def __init__(self, *args, **kwargs):
"""Initialize a new client for the Cloudkitty v1 API.""" """Initialize a new client for the Cloudkitty v1 API."""
self.auth_plugin = (kwargs.get('auth_plugin')
or ckclient.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'] = ckclient.get_auth_plugin(*args, **kwargs)
self.auth_plugin = kwargs.get('auth_plugin')
self.http_client = ckclient.construct_http_client(**kwargs)
self.modules = core.CloudkittyModuleManager(self.http_client) self.modules = core.CloudkittyModuleManager(self.http_client)
self.collector = collector.CollectorManager(self.http_client) self.collector = collector.CollectorManager(self.http_client)
self.reports = report.ReportManager(self.http_client) self.reports = report.ReportManager(self.http_client)