diff --git a/collectd_ceilometer/ceilometer/sender.py b/collectd_ceilometer/ceilometer/sender.py index a7c1c95..a4315b7 100644 --- a/collectd_ceilometer/ceilometer/sender.py +++ b/collectd_ceilometer/ceilometer/sender.py @@ -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 diff --git a/collectd_ceilometer/common/keystone_light.py b/collectd_ceilometer/common/keystone_light.py index a65ce32..3f3c095 100644 --- a/collectd_ceilometer/common/keystone_light.py +++ b/collectd_ceilometer/common/keystone_light.py @@ -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) { diff --git a/collectd_ceilometer/tests/common/test_keystone_light.py b/collectd_ceilometer/tests/common/test_keystone_light.py index 92b2f20..41c7f3d 100644 --- a/collectd_ceilometer/tests/common/test_keystone_light.py +++ b/collectd_ceilometer/tests/common/test_keystone_light.py @@ -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() diff --git a/doc/source/usage.rst b/doc/source/usage.rst index 92c9093..45174bc 100644 --- a/doc/source/usage.rst +++ b/doc/source/usage.rst @@ -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 diff --git a/releasenotes/notes/keystone-api-version-91dbfd93e93562d2.yaml b/releasenotes/notes/keystone-api-version-91dbfd93e93562d2.yaml new file mode 100644 index 0000000..fd34aee --- /dev/null +++ b/releasenotes/notes/keystone-api-version-91dbfd93e93562d2.yaml @@ -0,0 +1,5 @@ +--- +features: + - Added support for Keystone V3 API. +upgrade: + - Removed Keystone V2 support.