Merge "Remove Keystone V2 API support"

This commit is contained in:
Jenkins 2016-12-20 20:04:26 +00:00 committed by Gerrit Code Review
commit 54b4f156b2
5 changed files with 34 additions and 321 deletions

View File

@ -22,7 +22,6 @@ import threading
import requests
import six
from collectd_ceilometer.common.keystone_light import ClientV2
from collectd_ceilometer.common.keystone_light import ClientV3
from collectd_ceilometer.common.keystone_light import KeystoneException
from collectd_ceilometer.common.settings import Config
@ -78,20 +77,12 @@ class Sender(object):
# create a keystone client if it doesn't exist
if self._keystone is None:
cfg = Config.instance()
if cfg.OS_IDENTITY_API_VERSION == "2.0":
self._keystone = ClientV2(
auth_url=cfg.OS_AUTH_URL,
username=cfg.OS_USERNAME,
password=cfg.OS_PASSWORD,
tenant_name=cfg.OS_TENANT_NAME
)
else:
self._keystone = ClientV3(
auth_url=cfg.OS_AUTH_URL,
username=cfg.OS_USERNAME,
password=cfg.OS_PASSWORD,
tenant_name=cfg.OS_TENANT_NAME
)
self._keystone = ClientV3(
auth_url=cfg.OS_AUTH_URL,
username=cfg.OS_USERNAME,
password=cfg.OS_PASSWORD,
tenant_name=cfg.OS_TENANT_NAME
)
# store the authentication token
self._auth_token = self._keystone.auth_token

View File

@ -145,114 +145,6 @@ class ClientV3(object):
e, self._services)
class ClientV2(object):
"""Light weight client for the OpenStack Identity API V2.
:param string username: Username for authentication. (optional)
:param string password: Password for authentication.
:param string tenant_name: Tenant name. (optional)
:param string auth_url: Keystone service endpoint for authorization.
"""
def __init__(self, auth_url, username, password, tenant_name):
"""Initialize a new client"""
self.auth_url = auth_url
self.username = username
self.password = password
self.tenant_name = tenant_name
self._auth_token = None
self._services = None
@property
def auth_token(self):
"""Return token string usable for X-Auth-Token """
# actualize token
self.refresh()
return self._auth_token
@property
def services(self):
"""Return list of services retrieved from identity server """
return self._services
def _get_auth_data(self, headers=None):
"""Prepare auth data for request """
auth = {'password': self.password}
if self.username:
auth['username'] = self.username
return {'passwordCredentials': auth}
def _request_identity_data(self):
"""Will send (POST) and retrieve data from identity server """
headers = {'Accept': 'application/json'}
url = self.auth_url.rstrip('/') + '/tokens'
params = {'auth': self._get_auth_data(headers)}
if self.tenant_name:
params['auth']['tenantName'] = self.tenant_name
resp = requests.post(url, json=params, headers=headers)
try:
resp.raise_for_status()
resp_data = resp.json()['access']
except (KeyError, ValueError, requests.exceptions.HTTPError) as e:
raise InvalidResponse(e, resp)
return resp_data
def refresh(self):
"""Refresh token and services list (getting it from identity server) """
resp_data = self._request_identity_data()
try:
self._services = resp_data['serviceCatalog']
token = resp_data['token']
self._auth_token = token['id']
except (TypeError, KeyError, ValueError) as e:
raise InvalidResponse(e, resp_data)
return resp_data
def get_service_endpoint(self, name, urlkey="internalURL", region=None):
"""Return url endpoint of service
possible values of urlkey = 'adminURL' | 'publicURL' | 'internalURL'
provide region if more endpoints are available
"""
endpoints = None
try:
for service in self._services:
if service['name'] == name:
endpoints = service['endpoints']
break
if not endpoints:
raise MissingServices("Missing name '%s' in received services"
% name,
None, self._services)
# preselect default
endpoint = endpoints[0]
if region:
for ep in endpoints:
if ep['region'] == region:
endpoint = ep
break
return endpoint[urlkey].rstrip('/')
except (KeyError, ValueError) as e:
raise MissingServices("Missing data in received services",
e, self._services)
"""
Example of response (part only)
{

View File

@ -19,7 +19,6 @@
from __future__ import unicode_literals
from collectd_ceilometer import keystone_light
from collectd_ceilometer.keystone_light import ClientV2
from collectd_ceilometer.keystone_light import ClientV3
from collectd_ceilometer.keystone_light import MissingServices
import mock
@ -350,200 +349,3 @@ class KeystoneLightTestV3(unittest.TestCase):
with self.assertRaises(keystone_light.InvalidResponse):
client.refresh()
class KeystoneLightTestV2(unittest.TestCase):
"""Test the keystone light client with 2.0 keystone api"""
def setUp(self):
super(KeystoneLightTestV2, self).setUp()
self.test_authtoken = "c5bbb1c9a27e470fb482de2a718e08c2"
self.test_public_endpoint = "http://public_endpoint"
self.test_internal_endpoint = "http://iternal_endpoint"
self.test_region = "RegionOne"
response = {'access': {
"token": {
"issued_at": "2015-09-04T08:59:09.991646",
"expires": "2015-09-04T09:59:09Z",
"id": self.test_authtoken,
"tenant": {
"enabled": True,
"description": None,
"name": "service",
"id": "fdeec62f6c794c8dbfda448a83de9ce2"
},
"audit_ids": [
"Pig7hVfGQjSuUnt1Hc5mCg"
]
},
"serviceCatalog": [{
"endpoints_links": [],
"endpoints": [{
"adminURL": "http://10.237.214.74:8777/",
"region": self.test_region,
"publicURL": self.test_public_endpoint + '/',
"internalURL": self.test_internal_endpoint,
"id": "ac95b1a24a854ec7a4b63b08ed4cbd83"
}],
"type": "metering",
"name": "ceilometer"
}, ],
}}
self.mock_response = mock.Mock()
self.mock_response.json.return_value = response
@mock.patch('collectd_ceilometer.common.keystone_light.requests.post')
def test_refresh(self, mock_post):
"""Test refresh"""
mock_post.return_value = self.mock_response
client = ClientV2("test_auth_url", "test_username",
"test_password", "test_tenant")
client.refresh()
self.assertEqual(mock_post.call_count, 1)
self.assertEqual(client.auth_token, self.test_authtoken)
expected_args = {
'headers': {'Accept': 'application/json'},
'json': {
'auth': {
'tenantName': u'test_tenant',
'passwordCredentials': {
'username': u'test_username',
'password': u'test_password'
}
}
}
}
self.assertEqual(mock_post.call_args[0], (u'test_auth_url/tokens',))
self.assertEqual(mock_post.call_args[1], expected_args)
@mock.patch('collectd_ceilometer.common.keystone_light.requests.post')
def test_getservice_endpoint(self, mock_post):
"""Test getservice endpoint"""
mock_post.return_value = self.mock_response
client = ClientV2("test_auth_url", "test_username",
"test_password", "test_tenant")
client.refresh()
endpoint = client.get_service_endpoint('ceilometer')
self.assertEqual(endpoint, self.test_internal_endpoint)
endpoint = client.get_service_endpoint('ceilometer', 'publicURL')
self.assertEqual(endpoint, self.test_public_endpoint)
endpoint = client.get_service_endpoint('ceilometer', 'publicURL',
self.test_region)
self.assertEqual(endpoint, self.test_public_endpoint)
with self.assertRaises(MissingServices):
client.get_service_endpoint('badname')
@mock.patch('collectd_ceilometer.common.keystone_light.requests.post')
def test_getservice_endpoint_error(self, mock_post):
"""Test getservice endpoint error"""
response = {'access': {
"token": {
"id": "authtoken",
},
"serviceCatalog": [{
"endpoints_links": [],
"endpoints": [],
"type": "metering",
"missingname": "ceilometer"
},
],
}}
self.mock_response = mock.Mock()
self.mock_response.json.return_value = response
mock_post.return_value = self.mock_response
client = ClientV2("test_auth_url", "test_username",
"test_password", "test_tenant")
client.refresh()
with self.assertRaises(MissingServices):
client.get_service_endpoint('ceilometer')
@mock.patch('collectd_ceilometer.common.keystone_light.requests.post')
def test_invalidresponse_missing_access(self, mock_post):
"""Test invalid response: missing access"""
response = {'badresponse': None}
mock_response = mock.Mock()
mock_response.json.return_value = response
mock_post.return_value = mock_response
client = keystone_light.ClientV2("test_auth_url", "test_username",
"test_password", "test_tenant")
with self.assertRaises(keystone_light.InvalidResponse):
client.refresh()
@mock.patch('collectd_ceilometer.common.keystone_light.requests.post')
def test_invalidresponse_missing_servicecatalog(self, mock_post):
"""Test invalid response: missing servicecatalog"""
response = {'access': {
'token': None
}
}
mock_response = mock.Mock()
mock_response.json.return_value = response
mock_post.return_value = mock_response
client = keystone_light.ClientV2("test_auth_url", "test_username",
"test_password", "test_tenant")
with self.assertRaises(keystone_light.InvalidResponse):
client.refresh()
@mock.patch('collectd_ceilometer.common.keystone_light.requests.post')
def test_invalidresponse_missing_token(self, mock_post):
"""Test invalid response: missing token"""
response = {'access': {
"serviceCatalog": []
}}
mock_response = mock.Mock()
mock_response.json.return_value = response
mock_post.return_value = mock_response
client = keystone_light.ClientV2("test_auth_url", "test_username",
"test_password", "test_tenant")
with self.assertRaises(keystone_light.InvalidResponse):
client.refresh()
@mock.patch('collectd_ceilometer.common.keystone_light.requests.post')
def test_invalidresponse_missing_id(self, mock_post):
"""Test invalid response: missing id"""
response = {'access': {
"serviceCatalog": [],
"token": None
}, }
mock_response = mock.Mock()
mock_response.json.return_value = response
mock_post.return_value = mock_response
client = keystone_light.ClientV2("test_auth_url", "test_username",
"test_password", "test_tenant")
with self.assertRaises(keystone_light.InvalidResponse):
client.refresh()

View File

@ -59,3 +59,26 @@ COLLECTD_LOG_LEVEL
(debug|info|notice|warning|err) All log messages with lower log level than
this are going to be filtered out from the log file.
Default: info
Authenticating using Identity Server API v3
===========================================
following environment variables are used in this plugin for authentication
to Keystone API v3
OS_IDENTITY_API_VERSION
specifies version of keystone API used, should be set to 3 as 2.0 is
deprecated.
Default: 3
OS_AUTH_URL
url where keystone is listening
Default: based on $KEYSTONE_AUTH_URI/v$IDENTITY_API_VERSION
OS_PASSWORD
password for service tenant used for keystone authentication
Default: based on $SERVICE_PASSWORD
OS_TENANT_NAME
name of service tenant used for keystone authentication
Default: based on $SERVICE_TENANT_NAME

View File

@ -0,0 +1,5 @@
---
features:
- Added support for Keystone V3 API.
upgrade:
- Removed Keystone V2 support.