Support Keystone V3 with HttpClient

The http way doesn't work with keystone v3. This patch update the
HttpClient to support it.

Closes-bug: #1546280
Change-Id: Iefa1aafb796609aca076ed6ab73d02c92b9198d0
This commit is contained in:
wangxiyuan
2016-11-30 14:36:16 +08:00
parent 4eb63b8ef2
commit e1014faed1
3 changed files with 91 additions and 16 deletions

View File

@@ -205,7 +205,8 @@ class HTTPClient(object):
bypass_url=None, retries=None, bypass_url=None, retries=None,
http_log_debug=False, cacert=None, http_log_debug=False, cacert=None,
auth_system='keystone', auth_plugin=None, api_version=None, auth_system='keystone', auth_plugin=None, api_version=None,
logger=None): logger=None, user_domain_name='Default',
project_domain_name='Default'):
self.user = user self.user = user
self.password = password self.password = password
self.projectid = projectid self.projectid = projectid
@@ -236,6 +237,8 @@ class HTTPClient(object):
self.proxy_token = proxy_token self.proxy_token = proxy_token
self.proxy_tenant_id = proxy_tenant_id self.proxy_tenant_id = proxy_tenant_id
self.timeout = timeout self.timeout = timeout
self.user_domain_name = user_domain_name
self.project_domain_name = project_domain_name
if insecure: if insecure:
self.verify_cert = False self.verify_cert = False
@@ -419,8 +422,8 @@ class HTTPClient(object):
We may get redirected to another site, fail or actually get We may get redirected to another site, fail or actually get
back a service catalog with a token and our endpoints. back a service catalog with a token and our endpoints.
""" """
# content must always present
if resp.status_code == 200: # content must always present if resp.status_code == 200 or resp.status_code == 201:
try: try:
self.auth_url = url self.auth_url = url
self.auth_ref = access.create(resp=resp, body=body) self.auth_ref = access.create(resp=resp, body=body)
@@ -497,10 +500,10 @@ class HTTPClient(object):
path, query, frag)) path, query, frag))
auth_url = self.auth_url auth_url = self.auth_url
if self.version == "v2.0": if self.version == "v2.0" or self.version == "v3":
while auth_url: while auth_url:
if not self.auth_system or self.auth_system == 'keystone': if not self.auth_system or self.auth_system == 'keystone':
auth_url = self._v2_auth(auth_url) auth_url = self._v2_or_v3_auth(auth_url)
else: else:
auth_url = self._plugin_auth(auth_url) auth_url = self._plugin_auth(auth_url)
@@ -526,7 +529,7 @@ class HTTPClient(object):
except exceptions.AuthorizationFailure: except exceptions.AuthorizationFailure:
if auth_url.find('v2.0') < 0: if auth_url.find('v2.0') < 0:
auth_url = auth_url + '/v2.0' auth_url = auth_url + '/v2.0'
self._v2_auth(auth_url) self._v2_or_v3_auth(auth_url)
if self.bypass_url: if self.bypass_url:
self.set_management_url(self.bypass_url) self.set_management_url(self.bypass_url)
@@ -559,8 +562,27 @@ class HTTPClient(object):
def _plugin_auth(self, auth_url): def _plugin_auth(self, auth_url):
return self.auth_plugin.authenticate(self, auth_url) return self.auth_plugin.authenticate(self, auth_url)
def _v2_auth(self, url): def _v2_or_v3_auth(self, url):
"""Authenticate against a v2.0 auth service.""" """Authenticate against a v2.0 auth service."""
if self.version == "v3":
body = {
"auth": {
"identity": {
"methods": ["password"],
"password": {"user": {
"domain": {"name": self.user_domain_name},
"name": self.user,
"password": self.password}}},
}
}
scope = {"project": {"domain": {"name": self.project_domain_name}}}
if self.projectid:
scope['project']['name'] = self.projectid
elif self.tenant_id:
scope['project']['id'] = self.tenant_id
body["auth"]["scope"] = scope
else:
body = {"auth": { body = {"auth": {
"passwordCredentials": {"username": self.user, "passwordCredentials": {"username": self.user,
"password": self.password}}} "password": self.password}}}
@@ -569,13 +591,14 @@ class HTTPClient(object):
body['auth']['tenantName'] = self.projectid body['auth']['tenantName'] = self.projectid
elif self.tenant_id: elif self.tenant_id:
body['auth']['tenantId'] = self.tenant_id body['auth']['tenantId'] = self.tenant_id
self._authenticate(url, body) self._authenticate(url, body)
def _authenticate(self, url, body): def _authenticate(self, url, body):
"""Authenticate and extract the service catalog.""" """Authenticate and extract the service catalog."""
if self.version == 'v3':
token_url = url + "/auth/tokens"
else:
token_url = url + "/tokens" token_url = url + "/tokens"
# Make sure we follow redirects when trying to reach Keystone # Make sure we follow redirects when trying to reach Keystone
resp, body = self.request( resp, body = self.request(
token_url, token_url,

View File

@@ -11,6 +11,7 @@
# See the License for the specific language governing permissions and # See the License for the specific language governing permissions and
# limitations under the License. # limitations under the License.
import json
import mock import mock
import requests import requests
@@ -25,6 +26,12 @@ fake_response = utils.TestResponse({
}) })
mock_request = mock.Mock(return_value=(fake_response)) mock_request = mock.Mock(return_value=(fake_response))
fake_201_response = utils.TestResponse({
"status_code": 201,
"text": '{"hi": "there"}',
})
mock_201_request = mock.Mock(return_value=(fake_201_response))
refused_response = utils.TestResponse({ refused_response = utils.TestResponse({
"status_code": 400, "status_code": 400,
"text": '[Errno 111] Connection refused', "text": '[Errno 111] Connection refused',
@@ -293,6 +300,47 @@ class ClientTest(utils.TestCase):
test_auth_call() test_auth_call()
def test_auth_with_keystone_v3(self):
cl = get_authed_client()
cl.auth_url = 'http://example.com:5000/v3'
@mock.patch.object(cl, "_extract_service_catalog", mock.Mock())
@mock.patch.object(requests, "request", mock_201_request)
def test_auth_call():
cl.authenticate()
headers = {
"Content-Type": "application/json",
'Accept': 'application/json',
"User-Agent": cl.USER_AGENT
}
data = {
"auth": {
"scope": {
"project": {
"domain": {"name": "Default"},
"name": "project_id"
}
},
"identity": {
"methods": ["password"],
"password": {
"user": {"domain": {"name": "Default"},
"password": "password", "name": "username"
}
}
}
}
}
mock_201_request.assert_called_with(
"POST",
"http://example.com:5000/v3/auth/tokens",
headers=headers,
allow_redirects=True,
data=json.dumps(data),
**self.TEST_REQUEST_BASE)
test_auth_call()
def test_get_retry_timeout_error(self): def test_get_retry_timeout_error(self):
cl = get_authed_client(retries=1) cl = get_authed_client(retries=1)

View File

@@ -0,0 +1,4 @@
---
features:
- |
Support Keystone V3 authentication for httpClient.