Add cpid failover

If the client can't get the identity service ID from the cloud,
the client will use a hash of the endpoint's hostname as the CPID.

Change-Id: I0d61ccb05caf40131a2590bd89b6fce7c62cedd2
Implements-Spec: https://review.openstack.org/#/c/255607/
This commit is contained in:
Paul Van Eck 2015-12-17 13:33:02 -08:00
parent 30f38e8dfd
commit 2c355b84a4
3 changed files with 84 additions and 28 deletions

View File

@ -27,6 +27,7 @@ Tempest configuration file.
import argparse
import binascii
import ConfigParser
import hashlib
import itertools
import json
import logging
@ -42,6 +43,7 @@ from keystoneclient.v3 import client as ksclient3
import requests
import requests.exceptions
import six.moves
from six.moves.urllib import parse
from subunit_processor import SubunitProcessor
from list_parser import TestListParser
import yaml
@ -225,20 +227,32 @@ class RefstackClient:
# If a Key or Index Error was raised, one of the expected keys or
# indices for retrieving the identity service ID was not found.
except (KeyError, IndexError) as e:
raise RuntimeError('Unable to retrieve CPID from Keystone %s '
'catalog. The catalog or the identity '
'service endpoint was not '
'found.' % auth_version)
self.logger.warning('Unable to retrieve CPID from Keystone %s '
'catalog. The catalog or the identity '
'service endpoint was not '
'found.' % auth_version)
except Exception as e:
self.logger.error('Problems retreiving CPID from Keystone '
'using %s endpoint' % auth_version)
raise
self.logger.warning('Problems retrieving CPID from Keystone '
'using %s endpoint (%s)' % (auth_version,
args['auth_url']))
return self._generate_cpid_from_endpoint(args['auth_url'])
except ConfigParser.Error as e:
# Most likely a missing section or option in the config file.
self.logger.error("Invalid Config File: %s" % e)
exit(1)
def _generate_cpid_from_endpoint(self, endpoint):
'''This method will md5 hash the hostname of a Keystone endpoint to
generate a CPID.'''
self.logger.info('Creating hash from endpoint to use as CPID.')
url_parts = parse.urlparse(endpoint)
if url_parts.scheme not in ('http', 'https'):
raise ValueError('Invalid Keystone endpoint format. Make sure '
'the endpoint (%s) includes the URL scheme '
'(i.e. http/https).' % endpoint)
return hashlib.md5(url_parts.hostname).hexdigest()
def _form_result_content(self, cpid, duration, results):
'''This method will create the content for the request. The spec at
github.com/openstack/refstack/blob/master/specs/approved/api-v1.md.

View File

@ -1,10 +1,10 @@
[identity]
auth_version = v2
uri = 0.0.0.0:35357/v2.0
uri_v3 = 0.0.0.0:35357/v3
uri = http://0.0.0.0:35357/v2.0
uri_v3 = http://0.0.0.0:35357/v3
username = admin
password = test
tenant_id = admin_tenant_id
[identity-feature-enabled]
api_v2 = true
api_v2 = true

View File

@ -14,6 +14,7 @@
# under the License.
#
import hashlib
import json
import logging
import os
@ -174,7 +175,8 @@ class TestRefstackClient(unittest.TestCase):
cpid = client._get_cpid_from_keystone(client.conf)
self.ks2_client_builder.assert_called_with(
username='admin', tenant_id='admin_tenant_id',
password='test', auth_url='0.0.0.0:35357/v2.0', insecure=False
password='test', auth_url='http://0.0.0.0:35357/v2.0',
insecure=False
)
self.assertEqual('test-id', cpid)
@ -192,7 +194,8 @@ class TestRefstackClient(unittest.TestCase):
cpid = client._get_cpid_from_keystone(client.conf)
self.ks2_client_builder.assert_called_with(
username='admin', tenant_name='tenant_name',
password='test', auth_url='0.0.0.0:35357/v2.0', insecure=False
password='test', auth_url='http://0.0.0.0:35357/v2.0',
insecure=False
)
self.assertEqual('test-id', cpid)
@ -214,7 +217,8 @@ class TestRefstackClient(unittest.TestCase):
cpid = client._get_cpid_from_keystone(client.conf)
self.ks2_client_builder.assert_called_with(
username='admin', tenant_name='tenant_name',
password='test', auth_url='0.0.0.0:35357/v2.0', insecure=False
password='test', auth_url='http://0.0.0.0:35357/v2.0',
insecure=False
)
self.assertEqual('test-id', cpid)
@ -247,7 +251,8 @@ class TestRefstackClient(unittest.TestCase):
cpid = client._get_cpid_from_keystone(client.conf)
self.ks2_client_builder.assert_called_with(
username='admin', tenant_id='tenant_id',
password='test', auth_url='0.0.0.0:35357/v2.0', insecure=False
password='test', auth_url='http://0.0.0.0:35357/v2.0',
insecure=False
)
self.assertEqual('test-id', cpid)
@ -307,7 +312,8 @@ class TestRefstackClient(unittest.TestCase):
client._get_cpid_from_keystone(client.conf)
self.ks2_client_builder.assert_called_with(
username='admin', tenant_id='admin_tenant_id',
password='test', auth_url='0.0.0.0:35357/v2.0', insecure=True
password='test', auth_url='http://0.0.0.0:35357/v2.0',
insecure=True
)
def test_get_cpid_from_keystone_v3(self):
@ -325,7 +331,8 @@ class TestRefstackClient(unittest.TestCase):
cpid = client._get_cpid_from_keystone(client.conf)
self.ks3_client_builder.assert_called_with(
username='admin', tenant_name='tenant_name',
password='test', auth_url='0.0.0.0:35357/v3', insecure=False
password='test', auth_url='http://0.0.0.0:35357/v3',
insecure=False
)
self.assertEqual('test-id', cpid)
@ -339,6 +346,8 @@ class TestRefstackClient(unittest.TestCase):
client.tempest_dir = self.test_path
client._prep_test()
client._generate_cpid_from_endpoint = MagicMock()
# Test when the identity endpoints is empty
self.mock_ks2_client = MagicMock(
name='ks_client',
@ -349,8 +358,11 @@ class TestRefstackClient(unittest.TestCase):
'refstack_client.refstack_client.ksclient2.Client',
return_value=self.mock_ks2_client
)
with self.assertRaises(RuntimeError):
client._get_cpid_from_keystone(client.conf)
client._get_cpid_from_keystone(client.conf)
# Failover CPID should be generated.
client._generate_cpid_from_endpoint.assert_called_with(
'http://0.0.0.0:35357/v2.0'
)
# Test when the catalog is empty
self.mock_ks2_client = MagicMock(
@ -361,8 +373,11 @@ class TestRefstackClient(unittest.TestCase):
'refstack_client.refstack_client.ksclient2.Client',
return_value=self.mock_ks2_client
)
with self.assertRaises(RuntimeError):
client._get_cpid_from_keystone(client.conf)
client._get_cpid_from_keystone(client.conf)
# Failover CPID should be generated.
client._generate_cpid_from_endpoint.assert_called_with(
'http://0.0.0.0:35357/v2.0'
)
# Test when there is no service catalog
self.mock_ks2_client = MagicMock(name='ks_client', **{'auth_ref': {}})
@ -370,8 +385,11 @@ class TestRefstackClient(unittest.TestCase):
'refstack_client.refstack_client.ksclient2.Client',
return_value=self.mock_ks2_client
)
with self.assertRaises(RuntimeError):
client._get_cpid_from_keystone(client.conf)
client._get_cpid_from_keystone(client.conf)
# Failover CPID should be generated.
client._generate_cpid_from_endpoint.assert_called_with(
'http://0.0.0.0:35357/v2.0'
)
# Test when catalog has other non-identity services.
self.mock_ks2_client = MagicMock(
@ -402,6 +420,8 @@ class TestRefstackClient(unittest.TestCase):
client.conf.set('identity', 'tenant_name', 'tenant_name')
client.conf.set('identity-feature-enabled', 'api_v3', 'true')
client._generate_cpid_from_endpoint = MagicMock()
# Test when the identity ID is None.
self.mock_ks3_client = MagicMock(
name='ks_client',
@ -411,8 +431,11 @@ class TestRefstackClient(unittest.TestCase):
'refstack_client.refstack_client.ksclient3.Client',
return_value=self.mock_ks3_client
)
with self.assertRaises(RuntimeError):
client._get_cpid_from_keystone(client.conf)
client._get_cpid_from_keystone(client.conf)
# Failover CPID should be generated.
client._generate_cpid_from_endpoint.assert_called_with(
'http://0.0.0.0:35357/v3'
)
# Test when the catalog is empty.
self.mock_ks3_client = MagicMock(
@ -423,8 +446,11 @@ class TestRefstackClient(unittest.TestCase):
'refstack_client.refstack_client.ksclient3.Client',
return_value=self.mock_ks3_client
)
with self.assertRaises(RuntimeError):
client._get_cpid_from_keystone(client.conf)
client._get_cpid_from_keystone(client.conf)
# Failover CPID should be generated.
client._generate_cpid_from_endpoint.assert_called_with(
'http://0.0.0.0:35357/v3'
)
# Test when there is no service catalog.
self.mock_ks3_client = MagicMock(name='ks_client', **{'auth_ref': {}})
@ -432,8 +458,11 @@ class TestRefstackClient(unittest.TestCase):
'refstack_client.refstack_client.ksclient3.Client',
return_value=self.mock_ks3_client
)
with self.assertRaises(RuntimeError):
client._get_cpid_from_keystone(client.conf)
client._get_cpid_from_keystone(client.conf)
# Failover CPID should be generated.
client._generate_cpid_from_endpoint.assert_called_with(
'http://0.0.0.0:35357/v3'
)
#Test when catalog has other non-identity services.
self.mock_ks3_client = MagicMock(
@ -450,6 +479,19 @@ class TestRefstackClient(unittest.TestCase):
cpid = client._get_cpid_from_keystone(client.conf)
self.assertEqual('test-id2', cpid)
def test_generate_cpid_from_endpoint(self):
"""
Test that an endpoint's hostname is properly hashed.
"""
args = rc.parse_cli_args(self.mock_argv())
client = rc.RefstackClient(args)
cpid = client._generate_cpid_from_endpoint('http://some.url:5000/v2')
expected = hashlib.md5('some.url').hexdigest()
self.assertEqual(expected, cpid)
with self.assertRaises(ValueError):
client._generate_cpid_from_endpoint('some.url:5000/v2')
def test_form_result_content(self):
"""
Test that the request content is formed into the expected format.