Use adapter from keystoneclient
The adapter in keystoneclient is there to abstract the common client parameters that may be used when sending a request. We should use that rather than keeping our own methods. Closes-Bug: #1403726 Change-Id: I727ac76babb6f6aef1a0f619c011ad67a6e2fccf
This commit is contained in:
parent
5822d619e5
commit
799e288f48
@ -14,7 +14,6 @@
|
||||
# under the License.
|
||||
#
|
||||
|
||||
import abc
|
||||
try:
|
||||
import json
|
||||
except ImportError:
|
||||
@ -23,9 +22,8 @@ import logging
|
||||
import os
|
||||
|
||||
from keystoneclient import access
|
||||
from keystoneclient.auth.identity.base import BaseIdentityPlugin
|
||||
from keystoneclient import adapter
|
||||
import requests
|
||||
import six
|
||||
|
||||
from neutronclient.common import exceptions
|
||||
from neutronclient.common import utils
|
||||
@ -44,35 +42,14 @@ else:
|
||||
logging.getLogger("requests").setLevel(_requests_log_level)
|
||||
|
||||
|
||||
@six.add_metaclass(abc.ABCMeta)
|
||||
class AbstractHTTPClient(object):
|
||||
class HTTPClient(object):
|
||||
"""Handles the REST calls and responses, include authn."""
|
||||
|
||||
USER_AGENT = 'python-neutronclient'
|
||||
CONTENT_TYPE = 'application/json'
|
||||
|
||||
def request(self, url, method, body=None, content_type=None, headers=None,
|
||||
**kwargs):
|
||||
"""Request without authentication."""
|
||||
|
||||
headers = headers or {}
|
||||
content_type = content_type or self.CONTENT_TYPE
|
||||
headers.setdefault('Accept', content_type)
|
||||
if body:
|
||||
headers.setdefault('Content-Type', content_type)
|
||||
|
||||
return self._request(url, method, body=body, headers=headers, **kwargs)
|
||||
|
||||
@abc.abstractmethod
|
||||
def do_request(self, url, method, **kwargs):
|
||||
"""Request with authentication."""
|
||||
|
||||
@abc.abstractmethod
|
||||
def _request(self, url, method, body=None, headers=None, **kwargs):
|
||||
"""Request without authentication nor headers population."""
|
||||
|
||||
|
||||
class HTTPClient(AbstractHTTPClient):
|
||||
"""Handles the REST calls and responses, include authentication."""
|
||||
# 8192 Is the default max URI len for eventlet.wsgi.server
|
||||
MAX_URI_LEN = 8192
|
||||
|
||||
def __init__(self, username=None, user_id=None,
|
||||
tenant_name=None, tenant_id=None,
|
||||
@ -149,8 +126,16 @@ class HTTPClient(AbstractHTTPClient):
|
||||
elif not self.endpoint_url:
|
||||
self.endpoint_url = self._get_endpoint_url()
|
||||
|
||||
def _request(self, url, method, body=None, headers=None, **kwargs):
|
||||
def request(self, url, method, body=None, headers=None, **kwargs):
|
||||
"""Request without authentication."""
|
||||
|
||||
content_type = kwargs.pop('content_type', None) or 'application/json'
|
||||
headers = headers or {}
|
||||
headers.setdefault('Accept', content_type)
|
||||
|
||||
if body:
|
||||
headers.setdefault('Content-Type', content_type)
|
||||
|
||||
headers['User-Agent'] = self.USER_AGENT
|
||||
|
||||
resp = requests.request(
|
||||
@ -164,8 +149,17 @@ class HTTPClient(AbstractHTTPClient):
|
||||
|
||||
return resp, resp.text
|
||||
|
||||
def _check_uri_length(self, action):
|
||||
uri_len = len(self.endpoint_url) + len(action)
|
||||
if uri_len > self.MAX_URI_LEN:
|
||||
raise exceptions.RequestURITooLong(
|
||||
excess=uri_len - self.MAX_URI_LEN)
|
||||
|
||||
def do_request(self, url, method, **kwargs):
|
||||
# Ensure client always has correct uri - do not guesstimate anything
|
||||
self.authenticate_and_fetch_endpoint_url()
|
||||
self._check_uri_length(url)
|
||||
|
||||
# Perform the request once. If we get a 401 back then it
|
||||
# might be because the auth token expired, so try to
|
||||
# re-authenticate and try again. If it still fails, bail.
|
||||
@ -280,71 +274,67 @@ class HTTPClient(AbstractHTTPClient):
|
||||
'endpoint_url': self.endpoint_url}
|
||||
|
||||
|
||||
class SessionClient(AbstractHTTPClient):
|
||||
class SessionClient(adapter.Adapter):
|
||||
|
||||
def __init__(self,
|
||||
session,
|
||||
auth,
|
||||
interface=None,
|
||||
service_type=None,
|
||||
region_name=None):
|
||||
|
||||
self.session = session
|
||||
self.auth = auth
|
||||
self.interface = interface
|
||||
self.service_type = service_type
|
||||
self.region_name = region_name
|
||||
self.auth_token = None
|
||||
self.endpoint_url = None
|
||||
|
||||
def _request(self, url, method, body=None, headers=None, **kwargs):
|
||||
kwargs.setdefault('user_agent', self.USER_AGENT)
|
||||
kwargs.setdefault('auth', self.auth)
|
||||
def request(self, *args, **kwargs):
|
||||
kwargs.setdefault('authenticated', False)
|
||||
kwargs.setdefault('raise_exc', False)
|
||||
|
||||
endpoint_filter = kwargs.setdefault('endpoint_filter', {})
|
||||
endpoint_filter.setdefault('interface', self.interface)
|
||||
endpoint_filter.setdefault('service_type', self.service_type)
|
||||
endpoint_filter.setdefault('region_name', self.region_name)
|
||||
content_type = kwargs.pop('content_type', None) or 'application/json'
|
||||
|
||||
resp = self.session.request(url, method, data=body, headers=headers,
|
||||
**kwargs)
|
||||
headers = kwargs.setdefault('headers', {})
|
||||
headers.setdefault('Accept', content_type)
|
||||
|
||||
try:
|
||||
kwargs.setdefault('data', kwargs.pop('body'))
|
||||
except KeyError:
|
||||
pass
|
||||
|
||||
if kwargs.get('data'):
|
||||
headers.setdefault('Content-Type', content_type)
|
||||
|
||||
resp = super(SessionClient, self).request(*args, **kwargs)
|
||||
return resp, resp.text
|
||||
|
||||
def do_request(self, url, method, **kwargs):
|
||||
kwargs.setdefault('authenticated', True)
|
||||
return self.request(url, method, **kwargs)
|
||||
|
||||
def authenticate(self):
|
||||
# This method is provided for backward compatibility only.
|
||||
# We only care about setting the service endpoint.
|
||||
self.endpoint_url = self.session.get_endpoint(
|
||||
self.auth,
|
||||
service_type=self.service_type,
|
||||
region_name=self.region_name,
|
||||
interface=self.interface)
|
||||
@property
|
||||
def endpoint_url(self):
|
||||
# NOTE(jamielennox): This is used purely by the CLI and should be
|
||||
# removed when the CLI gets smarter.
|
||||
return self.get_endpoint()
|
||||
|
||||
def authenticate_and_fetch_endpoint_url(self):
|
||||
# This method is provided for backward compatibility only.
|
||||
# We only care about setting the service endpoint.
|
||||
self.authenticate()
|
||||
@property
|
||||
def auth_token(self):
|
||||
# NOTE(jamielennox): This is used purely by the CLI and should be
|
||||
# removed when the CLI gets smarter.
|
||||
return self.get_token()
|
||||
|
||||
def authenticate(self):
|
||||
# NOTE(jamielennox): This is used purely by the CLI and should be
|
||||
# removed when the CLI gets smarter.
|
||||
self.get_token()
|
||||
|
||||
def get_auth_info(self):
|
||||
# This method is provided for backward compatibility only.
|
||||
if not isinstance(self.auth, BaseIdentityPlugin):
|
||||
msg = ('Auth info not available. Auth plugin is not an identity '
|
||||
'auth plugin.')
|
||||
raise exceptions.NeutronClientException(message=msg)
|
||||
access_info = self.auth.get_access(self.session)
|
||||
endpoint_url = self.auth.get_endpoint(self.session,
|
||||
service_type=self.service_type,
|
||||
region_name=self.region_name,
|
||||
interface=self.interface)
|
||||
return {'auth_token': access_info.auth_token,
|
||||
'auth_tenant_id': access_info.tenant_id,
|
||||
'auth_user_id': access_info.user_id,
|
||||
'endpoint_url': endpoint_url}
|
||||
auth_info = {'auth_token': self.auth_token,
|
||||
'endpoint_url': self.endpoint_url}
|
||||
|
||||
# NOTE(jamielennox): This is the best we can do here. It will work
|
||||
# with identity plugins which is the primary case but we should
|
||||
# deprecate it's usage as much as possible.
|
||||
try:
|
||||
get_access = (self.auth or self.session.auth).get_access
|
||||
except AttributeError:
|
||||
pass
|
||||
else:
|
||||
auth_ref = get_access(self.session)
|
||||
|
||||
auth_info['auth_tenant_id'] = auth_ref.project_id
|
||||
auth_info['auth_user_id'] = auth_ref.user_id
|
||||
|
||||
return auth_info
|
||||
|
||||
|
||||
# FIXME(bklei): Should refactor this to use kwargs and only
|
||||
@ -366,14 +356,15 @@ def construct_http_client(username=None,
|
||||
ca_cert=None,
|
||||
service_type='network',
|
||||
session=None,
|
||||
auth=None):
|
||||
**kwargs):
|
||||
|
||||
if session:
|
||||
kwargs.setdefault('user_agent', 'python-neutronclient')
|
||||
kwargs.setdefault('interface', endpoint_type)
|
||||
return SessionClient(session=session,
|
||||
auth=auth,
|
||||
interface=endpoint_type,
|
||||
service_type=service_type,
|
||||
region_name=region_name)
|
||||
region_name=region_name,
|
||||
**kwargs)
|
||||
else:
|
||||
# FIXME(bklei): username and password are now optional. Need
|
||||
# to test that they were provided in this mode. Should also
|
||||
|
@ -316,7 +316,6 @@ class CLITestAuthKeystone(testtools.TestCase):
|
||||
auth_url=AUTH_URL, region_name=REGION,
|
||||
session=auth_session, auth=auth_plugin)
|
||||
|
||||
self.client.authenticate()
|
||||
self.assertEqual(self.client.endpoint_url, PUBLIC_ENDPOINT_URL)
|
||||
|
||||
# Test admin url
|
||||
@ -325,7 +324,6 @@ class CLITestAuthKeystone(testtools.TestCase):
|
||||
auth_url=AUTH_URL, region_name=REGION, endpoint_type='adminURL',
|
||||
session=auth_session, auth=auth_plugin)
|
||||
|
||||
self.client.authenticate()
|
||||
self.assertEqual(self.client.endpoint_url, ADMIN_ENDPOINT_URL)
|
||||
|
||||
# Test public url
|
||||
@ -334,7 +332,6 @@ class CLITestAuthKeystone(testtools.TestCase):
|
||||
auth_url=AUTH_URL, region_name=REGION, endpoint_type='publicURL',
|
||||
session=auth_session, auth=auth_plugin)
|
||||
|
||||
self.client.authenticate()
|
||||
self.assertEqual(self.client.endpoint_url, PUBLIC_ENDPOINT_URL)
|
||||
|
||||
# Test internal url
|
||||
@ -343,7 +340,6 @@ class CLITestAuthKeystone(testtools.TestCase):
|
||||
auth_url=AUTH_URL, region_name=REGION, endpoint_type='internalURL',
|
||||
session=auth_session, auth=auth_plugin)
|
||||
|
||||
self.client.authenticate()
|
||||
self.assertEqual(self.client.endpoint_url, INTERNAL_ENDPOINT_URL)
|
||||
|
||||
# Test url that isn't found in the service catalog
|
||||
@ -354,7 +350,7 @@ class CLITestAuthKeystone(testtools.TestCase):
|
||||
|
||||
self.assertRaises(
|
||||
ks_exceptions.EndpointNotFound,
|
||||
self.client.authenticate)
|
||||
getattr, self.client, 'endpoint_url')
|
||||
|
||||
def test_strip_credentials_from_log(self):
|
||||
m = self.requests.post(AUTH_URL + '/tokens', json=KS_TOKEN_RESULT)
|
||||
|
@ -519,13 +519,15 @@ class CLITestV20NetworkJSON(test_cli20.CLITestV20Base):
|
||||
filters, response = self._build_test_data(data)
|
||||
|
||||
# 1 char of extra URI len will cause a split in 2 requests
|
||||
self.mox.StubOutWithMock(self.client, "_check_uri_length")
|
||||
self.client._check_uri_length(mox.IgnoreArg()).AndRaise(
|
||||
self.mox.StubOutWithMock(self.client.httpclient,
|
||||
"_check_uri_length")
|
||||
self.client.httpclient._check_uri_length(mox.IgnoreArg()).AndRaise(
|
||||
exceptions.RequestURITooLong(excess=1))
|
||||
|
||||
for data in sub_data_lists:
|
||||
filters, response = self._build_test_data(data)
|
||||
self.client._check_uri_length(mox.IgnoreArg()).AndReturn(None)
|
||||
self.client.httpclient._check_uri_length(
|
||||
mox.IgnoreArg()).AndReturn(None)
|
||||
self.client.httpclient.request(
|
||||
test_cli20.MyUrlComparator(
|
||||
test_cli20.end_url(
|
||||
|
@ -265,13 +265,14 @@ class CLITestV20SecurityGroupsJSON(test_cli20.CLITestV20Base):
|
||||
def test_extend_list_exceed_max_uri_len(self):
|
||||
def mox_calls(path, data):
|
||||
# 1 char of extra URI len will cause a split in 2 requests
|
||||
self.mox.StubOutWithMock(self.client, '_check_uri_length')
|
||||
self.client._check_uri_length(mox.IgnoreArg()).AndRaise(
|
||||
self.mox.StubOutWithMock(self.client.httpclient,
|
||||
'_check_uri_length')
|
||||
self.client.httpclient._check_uri_length(mox.IgnoreArg()).AndRaise(
|
||||
exceptions.RequestURITooLong(excess=1))
|
||||
responses = self._build_test_data(data, excess=1)
|
||||
|
||||
for item in responses:
|
||||
self.client._check_uri_length(
|
||||
self.client.httpclient._check_uri_length(
|
||||
mox.IgnoreArg()).AndReturn(None)
|
||||
self.client.httpclient.request(
|
||||
test_cli20.end_url(path, item['filter']),
|
||||
|
@ -256,8 +256,6 @@ class Client(object):
|
||||
'net_partitions': 'net_partition',
|
||||
'packet_filters': 'packet_filter',
|
||||
}
|
||||
# 8192 Is the default max URI len for eventlet.wsgi.server
|
||||
MAX_URI_LEN = 8192
|
||||
|
||||
def get_attr_metadata(self):
|
||||
if self.format == 'json':
|
||||
@ -1219,12 +1217,6 @@ class Client(object):
|
||||
# Raise the appropriate exception
|
||||
exception_handler_v20(status_code, des_error_body)
|
||||
|
||||
def _check_uri_length(self, action):
|
||||
uri_len = len(self.httpclient.endpoint_url) + len(action)
|
||||
if uri_len > self.MAX_URI_LEN:
|
||||
raise exceptions.RequestURITooLong(
|
||||
excess=uri_len - self.MAX_URI_LEN)
|
||||
|
||||
def do_request(self, method, action, body=None, headers=None, params=None):
|
||||
# Add format and tenant_id
|
||||
action += ".%s" % self.format
|
||||
@ -1232,9 +1224,6 @@ class Client(object):
|
||||
if type(params) is dict and params:
|
||||
params = utils.safe_encode_dict(params)
|
||||
action += '?' + urlparse.urlencode(params, doseq=1)
|
||||
# Ensure client always has correct uri - do not guesstimate anything
|
||||
self.httpclient.authenticate_and_fetch_endpoint_url()
|
||||
self._check_uri_length(action)
|
||||
|
||||
if body:
|
||||
body = self.serialize(body)
|
||||
|
Loading…
x
Reference in New Issue
Block a user