# Copyright 2018 Red Hat, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import importlib import warnings import os_service_types from openstack import _log from openstack import service_description _logger = _log.setup_logging('openstack') _service_type_manager = os_service_types.ServiceTypes() def make_names(): imports = ['from openstack import service_description'] services = [] for service in _service_type_manager.services: service_type = service['service_type'] if service_type == 'ec2-api': # NOTE(mordred) It doesn't make any sense to use ec2-api # from openstacksdk. The credentials API calls are all calls # on identity endpoints. continue desc_class = _find_service_description_class(service_type) st = service_type.replace('-', '_') if desc_class.__module__ != 'openstack.service_description': base_mod, dm = desc_class.__module__.rsplit('.', 1) imports.append(f'from {base_mod} import {dm}') else: dm = 'service_description' dc = desc_class.__name__ services.append( "{st} = {dm}.{dc}(service_type='{service_type}')".format( st=st, dm=dm, dc=dc, service_type=service_type ), ) # Register the descriptor class with every known alias. Don't # add doc strings though - although they are supported, we don't # want to give anybody any bad ideas. Making a second descriptor # does not introduce runtime cost as the descriptors all use # the same _proxies dict on the instance. for alias_name in _get_aliases(st): if alias_name[-1].isdigit(): continue services.append(f'{alias_name} = {st}') services.append('') print("# Generated file, to change, run tools/print-services.py") for imp in sorted(imports): print(imp) print('\n') print("class ServicesMixin:\n") for service in services: if service: print(f" {service}") else: print() def _get_aliases(service_type, aliases=None): # We make connection attributes for all official real type names # and aliases. Three services have names they were called by in # openstacksdk that are not covered by Service Types Authority aliases. # Include them here - but take heed, no additional values should ever # be added to this list. # that were only used in openstacksdk resource naming. LOCAL_ALIASES = { 'baremetal': 'bare_metal', 'block_storage': 'block_store', 'clustering': 'cluster', } all_types = set(_service_type_manager.get_aliases(service_type)) if aliases: all_types.update(aliases) if service_type in LOCAL_ALIASES: all_types.add(LOCAL_ALIASES[service_type]) all_aliases = set() for alias in all_types: all_aliases.add(alias.replace('-', '_')) return all_aliases def _find_service_description_class(service_type): package_name = f'openstack.{service_type}'.replace('-', '_') module_name = service_type.replace('-', '_') + '_service' class_name = ''.join( [part.capitalize() for part in module_name.split('_')] ) # We have some exceptions :( # This should have been called 'shared-filesystem' if service_type == 'shared-file-system': class_name = 'SharedFilesystemService' try: import_name = '.'.join([package_name, module_name]) service_description_module = importlib.import_module(import_name) except ImportError as e: # ImportWarning is ignored by default. This warning is here # as an opt-in for people trying to figure out why something # didn't work. warnings.warn( f"Could not import {service_type} service description: {str(e)}", ImportWarning, ) return service_description.ServiceDescription # There are no cases in which we should have a module but not the class # inside it. service_description_class = getattr(service_description_module, class_name) return service_description_class make_names()