Don't use name of a service as a key

Name of a service shouldn't be used as a key in structures as the name is
a user-facing name and it's not intended to be machine-parseable:
https://docs.openstack.org/keystone/stein/contributor/service-catalog.html#services

Story: 2006892
Task: 37526

Change-Id: I9621aee957e8a043f6ac251b96abafd80ef167f1
This commit is contained in:
Martin Kopec 2019-11-19 07:49:55 +00:00
parent b84544cf0a
commit 6a616e5db2
25 changed files with 96 additions and 87 deletions

View File

@ -19,8 +19,8 @@ from config_tempest.services.base import Service
class AlarmingService(Service): class AlarmingService(Service):
@staticmethod @staticmethod
def get_service_name(): def get_service_type():
return ['aodh'] return ['alarming']
@staticmethod @staticmethod
def get_codename(): def get_codename():

View File

@ -22,7 +22,7 @@ class Ec2Service(Service):
conf.set('aws', 'ec2_url', self.service_url) conf.set('aws', 'ec2_url', self.service_url)
@staticmethod @staticmethod
def get_service_name(): def get_service_type():
return ['ec2'] return ['ec2']
@ -32,5 +32,5 @@ class S3Service(Service):
conf.set('aws', 's3_url', self.service_url) conf.set('aws', 's3_url', self.service_url)
@staticmethod @staticmethod
def get_service_name(): def get_service_type():
return ['s3'] return ['s3']

View File

@ -19,8 +19,8 @@ from config_tempest.services.base import Service
class BaremetalService(Service): class BaremetalService(Service):
@staticmethod @staticmethod
def get_service_name(): def get_service_type():
return ['ironic'] return ['baremetal']
@staticmethod @staticmethod
def get_codename(): def get_codename():

View File

@ -89,11 +89,11 @@ class Service(object):
return self.extensions return self.extensions
@staticmethod @staticmethod
def get_service_name(): def get_service_type():
"""Return the service name. """Return the service type.
This returns a list because you can have different services for the This returns a list because you can have services with more types,
same type, like volume, volumev2, volumev3 like volume, volumev2, volumev3.
""" """
return [] return []

View File

@ -34,8 +34,8 @@ class MeteringService(Service):
conf.set('service_available', 'ceilometer', 'True') conf.set('service_available', 'ceilometer', 'True')
@staticmethod @staticmethod
def get_service_name(): def get_service_type():
return ['ceilometer'] return ['metering']
@staticmethod @staticmethod
def get_codename(): def get_codename():

View File

@ -62,8 +62,8 @@ class ComputeService(VersionedService):
str(is_service(**{'type': 'key-manager'}))) str(is_service(**{'type': 'key-manager'})))
@staticmethod @staticmethod
def get_service_name(): def get_service_type():
return ['nova'] return ['compute']
@staticmethod @staticmethod
def get_codename(): def get_codename():

View File

@ -19,8 +19,8 @@ from config_tempest.services.base import Service
class DataProcessingService(Service): class DataProcessingService(Service):
@staticmethod @staticmethod
def get_service_name(): def get_service_type():
return ['sahara'] return ['data-processing']
@staticmethod @staticmethod
def get_codename(): def get_codename():

View File

@ -19,8 +19,8 @@ from config_tempest.services.base import Service
class DatabaseService(Service): class DatabaseService(Service):
@staticmethod @staticmethod
def get_service_name(): def get_service_type():
return ['trove'] return ['database']
@staticmethod @staticmethod
def get_codename(): def get_codename():

View File

@ -19,8 +19,8 @@ from config_tempest.services.base import Service
class DnsService(Service): class DnsService(Service):
@staticmethod @staticmethod
def get_service_name(): def get_service_type():
return ['designate'] return ['dns']
@staticmethod @staticmethod
def get_codename(): def get_codename():

View File

@ -19,8 +19,8 @@ from config_tempest.services.base import Service
class EventService(Service): class EventService(Service):
@staticmethod @staticmethod
def get_service_name(): def get_service_type():
return ['panko'] return ['event']
@staticmethod @staticmethod
def get_codename(): def get_codename():

View File

@ -61,8 +61,8 @@ class IdentityService(VersionedService):
return ['v2', 'v3'] return ['v2', 'v3']
@staticmethod @staticmethod
def get_service_name(): def get_service_type():
return ['keystone'] return ['identity']
def set_identity_v3_extensions(self): def set_identity_v3_extensions(self):
"""Returns discovered identity v3 extensions """Returns discovered identity v3 extensions

View File

@ -74,8 +74,8 @@ class ImageService(VersionedService):
return ['v1', 'v2'] return ['v1', 'v2']
@staticmethod @staticmethod
def get_service_name(): def get_service_type():
return ['glance'] return ['image']
@staticmethod @staticmethod
def get_codename(): def get_codename():

View File

@ -19,8 +19,8 @@ from config_tempest.services.base import Service
class MessagingService(Service): class MessagingService(Service):
@staticmethod @staticmethod
def get_service_name(): def get_service_type():
return ['zaqar'] return ['messaging']
@staticmethod @staticmethod
def get_codename(): def get_codename():

View File

@ -19,8 +19,8 @@ from config_tempest.services.base import Service
class MetricService(Service): class MetricService(Service):
@staticmethod @staticmethod
def get_service_name(): def get_service_type():
return ['gnocchi'] return ['metric']
@staticmethod @staticmethod
def get_codename(): def get_codename():

View File

@ -93,8 +93,8 @@ class NetworkService(VersionedService):
'discovered and must be specified') 'discovered and must be specified')
@staticmethod @staticmethod
def get_service_name(): def get_service_type():
return ['neutron'] return ['network']
@staticmethod @staticmethod
def get_codename(): def get_codename():

View File

@ -106,8 +106,8 @@ class ObjectStorageService(Service):
self.list_create_roles(conf, self.client.roles) self.list_create_roles(conf, self.client.roles)
@staticmethod @staticmethod
def get_service_name(): def get_service_type():
return ['swift'] return ['object-store']
@staticmethod @staticmethod
def get_codename(): def get_codename():

View File

@ -28,8 +28,8 @@ class LoadBalancerService(VersionedService):
conf.set('load_balancer', 'RBAC_test_type', 'owner_or_admin') conf.set('load_balancer', 'RBAC_test_type', 'owner_or_admin')
@staticmethod @staticmethod
def get_service_name(): def get_service_type():
return ['octavia'] return ['load-balancer']
@staticmethod @staticmethod
def get_codename(): def get_codename():

View File

@ -22,8 +22,8 @@ from config_tempest.services.base import Service
class OrchestrationService(Service): class OrchestrationService(Service):
@staticmethod @staticmethod
def get_service_name(): def get_service_type():
return ['heat'] return ['orchestration']
@staticmethod @staticmethod
def get_codename(): def get_codename():

View File

@ -64,39 +64,50 @@ class Services(object):
def get_available_services(self): def get_available_services(self):
try: try:
services = self._clients.service_client.list_services()['services'] services = self._clients.service_client.list_services()['services']
return {s['name']: s['type'] for s in services}
except exceptions.Forbidden: except exceptions.Forbidden:
C.LOG.warning("User has no permissions to list services, using " C.LOG.warning("User has no permissions to list services, using "
"catalog. Services without endpoint will not be " "catalog. Services without endpoint will not be "
"discovered.") "discovered.")
token, auth_data = self._clients.auth_provider.get_auth() services = self.catalog
return {s['name']: s['type'] for s return services
in auth_data[self.service_catalog]}
def get_service_data(self, s_name, s_type):
for s in self.catalog:
if s['name'] == s_name and s['type'] == s_type:
return s
return None
def discover(self): def discover(self):
token, auth_data = self._clients.auth_provider.get_auth()
auth_entries = {e['type']: e for e in auth_data[self.service_catalog]}
# We loop through the classes we have for each service, and if we find # We loop through the classes we have for each service, and if we find
# a class that match a service enabled, we add it in our services list. # a class that match a service enabled, we add it in our services list.
# some services doesn't have endpoints, so we need to check first # some services doesn't have endpoints, so we need to check first
for s_class in self.service_classes: for s_class in self.service_classes:
s_names = s_class.get_service_name() s_types = s_class.get_service_type()
for s_name in s_names: for s_type in s_types:
s_type = self.available_services.get(s_name, None) s_name = [t['name'] for t in self.available_services
if s_type: if t['type'] == s_type]
endpoint_data = auth_entries.get(s_type, None) if s_name:
# In the general case, there should only be one service in
# a deployment per service type
# https://docs.openstack.org/keystone/latest/contributor/
# service-catalog.html#services
if len(s_name) > 1:
C.LOG.warning("There are more service names ('%s') for"
" '%s' service type, which is undefined"
" behavior. Continuing with '%s'.",
str(s_name), s_type, s_name[0])
s_name = s_name[0]
service_data = self.get_service_data(s_name, s_type)
url = None url = None
if not endpoint_data: if not service_data:
C.LOG.Warning('No endpoint data found for {}'.format( C.LOG.warning('No endpoint data found for {}'.format(
s_name)) s_name))
else: else:
url = self.parse_endpoints(self.get_endpoints( url = self.parse_endpoints(self.get_endpoints(
endpoint_data), s_type) service_data), s_type)
# Create the service class and add it to services list # Create the service class and add it to services list
service = s_class(s_name, s_type, url, token, service = s_class(s_name, s_type, url, self.token,
self._ssl_validation, self._ssl_validation,
self._clients.get_service_client( self._clients.get_service_client(
s_type)) s_type))
@ -153,11 +164,13 @@ class Services(object):
def set_catalog_and_url(self): def set_catalog_and_url(self):
if self._creds.api_version == 3: if self._creds.api_version == 3:
self.service_catalog = 'catalog' service_catalog = 'catalog'
self.public_url = 'url' self.public_url = 'url'
else: else:
self.service_catalog = 'serviceCatalog' service_catalog = 'serviceCatalog'
self.public_url = 'publicURL' self.public_url = 'publicURL'
self.token, auth_data = self._clients.auth_provider.get_auth()
self.catalog = auth_data[service_catalog]
def parse_endpoints(self, ep, name): def parse_endpoints(self, ep, name):
"""Parse an endpoint(s). """Parse an endpoint(s).
@ -214,15 +227,16 @@ class Services(object):
:param kwargs: Search parameters (accepts service name or type) :param kwargs: Search parameters (accepts service name or type)
:rtype: boolean :rtype: boolean
""" """
a_s = self.available_services
if kwargs.get('name'): if kwargs.get('name'):
if kwargs.get('name') not in self.available_services.keys(): if [s for s in a_s if s['name'] == kwargs.get('name')]:
return False return True
return True return False
if kwargs.get('type'): if kwargs.get('type'):
if kwargs.get('type') not in self.available_services.values(): if [s for s in a_s if s['type'] == kwargs.get('type')]:
return False return True
return True return False
return False return False
def post_configuration(self): def post_configuration(self):

View File

@ -35,5 +35,5 @@ class ShareService(VersionedService):
return 'share' return 'share'
@staticmethod @staticmethod
def get_service_name(): def get_service_type():
return ['manila', 'manilav2'] return ['share', 'sharev2']

View File

@ -19,8 +19,8 @@ from config_tempest.services.base import Service
class TelemetryService(Service): class TelemetryService(Service):
@staticmethod @staticmethod
def get_service_name(): def get_service_type():
return ['ceilometer'] return ['telemetry']
@staticmethod @staticmethod
def get_codename(): def get_codename():

View File

@ -56,8 +56,8 @@ class VolumeService(VersionedService):
return 'volume' return 'volume'
@staticmethod @staticmethod
def get_service_name(): def get_service_type():
return ['cinderv2', 'cinderv3'] return ['volumev2', 'volumev3']
def post_configuration(self, conf, is_service): def post_configuration(self, conf, is_service):
# Verify if the cinder backup service is enabled # Verify if the cinder backup service is enabled

View File

@ -19,8 +19,8 @@ from config_tempest.services.base import Service
class Workflowv2Service(Service): class Workflowv2Service(Service):
@staticmethod @staticmethod
def get_service_name(): def get_service_type():
return ['mistral'] return ['workflow']
@staticmethod @staticmethod
def get_codename(): def get_codename():

View File

@ -26,9 +26,11 @@ class TestEc2Service(BaseConfigTempestTest):
FAKE_URL = "http://10.200.16.10:8774/" FAKE_URL = "http://10.200.16.10:8774/"
@mock.patch('config_tempest.services.services.Services.discover') @mock.patch('config_tempest.services.services.Services.discover')
@mock.patch('config_tempest.services.services.Services.'
'set_catalog_and_url')
@mock.patch('config_tempest.services.services.Services.' @mock.patch('config_tempest.services.services.Services.'
'get_available_services') 'get_available_services')
def setUp(self, mock_set_avail, mock_discover): def setUp(self, mock_set_avail, mock_catalog, mock_discover):
super(TestEc2Service, self).setUp() super(TestEc2Service, self).setUp()
conf = self._get_conf('v2', 'v3') conf = self._get_conf('v2', 'v3')
self.clients = self._get_clients(conf) self.clients = self._get_clients(conf)
@ -46,9 +48,11 @@ class TestS3Service(BaseConfigTempestTest):
FAKE_URL = "http://10.200.16.10:8774/" FAKE_URL = "http://10.200.16.10:8774/"
@mock.patch('config_tempest.services.services.Services.discover') @mock.patch('config_tempest.services.services.Services.discover')
@mock.patch('config_tempest.services.services.Services.'
'set_catalog_and_url')
@mock.patch('config_tempest.services.services.Services.' @mock.patch('config_tempest.services.services.Services.'
'get_available_services') 'get_available_services')
def setUp(self, mock_set_avail, mock_discover): def setUp(self, mock_set_avail, mock_catalog, mock_discover):
super(TestS3Service, self).setUp() super(TestS3Service, self).setUp()
conf = self._get_conf('v2', 'v3') conf = self._get_conf('v2', 'v3')
self.clients = self._get_clients(conf) self.clients = self._get_clients(conf)

View File

@ -37,10 +37,13 @@ class TestServices(BaseConfigTempestTest):
super(TestServices, self).setUp() super(TestServices, self).setUp()
@mock.patch('config_tempest.services.services.Services.discover') @mock.patch('config_tempest.services.services.Services.discover')
@mock.patch('config_tempest.services.services.Services.'
'set_catalog_and_url')
@mock.patch('config_tempest.services.services.Services.' @mock.patch('config_tempest.services.services.Services.'
'get_available_services') 'get_available_services')
def _create_services_instance(self, mock_avail, mock_discover, v2=False): def _create_services_instance(self, mock_avail, mock_catalog,
mock_avail.return_value = {'my_service': 'my_type'} mock_discover, v2=False):
mock_avail.return_value = [{'name': 'my_service', 'type': 'my_type'}]
conf = self._get_conf('v2', 'v3') conf = self._get_conf('v2', 'v3')
creds = self._get_creds(conf, v2=v2) creds = self._get_creds(conf, v2=v2)
clients = mock.Mock() clients = mock.Mock()
@ -74,18 +77,6 @@ class TestServices(BaseConfigTempestTest):
resp = services.get_endpoints({'endpoints': []}) resp = services.get_endpoints({'endpoints': []})
self.assertEqual(resp, []) self.assertEqual(resp, [])
def test_set_catalog_and_url(self):
# api version = 2
services = self._create_services_instance(v2=True)
services.set_catalog_and_url()
self.assertEqual(services.service_catalog, 'serviceCatalog')
self.assertEqual(services.public_url, 'publicURL')
# api version = 3
services = self._create_services_instance()
services.set_catalog_and_url()
self.assertEqual(services.service_catalog, 'catalog')
self.assertEqual(services.public_url, 'url')
def test_parse_endpoints_empty(self): def test_parse_endpoints_empty(self):
services = self._create_services_instance() services = self._create_services_instance()
services.public_url = "url" services.public_url = "url"