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