Use generated list of services instead of metaclass
The metaclass makes it pretty impossible to do type annotations on anything. Turn the metaclass into a utility script and then generate the list of mappings. Change-Id: Iccd5ee34e364cd6f53c0a8934e20442c022844e8
This commit is contained in:
parent
8b85e8c954
commit
c9fba05bf6
@ -1,126 +0,0 @@
|
||||
# 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()
|
||||
_DOC_TEMPLATE = (
|
||||
":class:`{class_name}` for {service_type} aka {project}")
|
||||
_PROXY_TEMPLATE = """Proxy for {service_type} aka {project}
|
||||
|
||||
This proxy object could be an instance of
|
||||
{class_doc_strings}
|
||||
depending on client configuration and which version of the service is
|
||||
found on remotely on the cloud.
|
||||
"""
|
||||
|
||||
|
||||
class ConnectionMeta(type):
|
||||
def __new__(meta, name, bases, dct):
|
||||
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)
|
||||
descriptor_args = {'service_type': service_type}
|
||||
|
||||
if not desc_class.supported_versions:
|
||||
doc = _DOC_TEMPLATE.format(
|
||||
class_name="{service_type} Proxy".format(
|
||||
service_type=service_type),
|
||||
**service)
|
||||
elif len(desc_class.supported_versions) == 1:
|
||||
supported_version = list(
|
||||
desc_class.supported_versions.keys())[0]
|
||||
doc = _DOC_TEMPLATE.format(
|
||||
class_name="{service_type} Proxy <{name}>".format(
|
||||
service_type=service_type, name=supported_version),
|
||||
**service)
|
||||
else:
|
||||
class_doc_strings = "\n".join([
|
||||
":class:`{class_name}`".format(
|
||||
class_name=proxy_class.__name__)
|
||||
for proxy_class in desc_class.supported_versions.values()])
|
||||
doc = _PROXY_TEMPLATE.format(
|
||||
class_doc_strings=class_doc_strings, **service)
|
||||
descriptor = desc_class(**descriptor_args)
|
||||
descriptor.__doc__ = doc
|
||||
st = service_type.replace('-', '_')
|
||||
dct[st] = descriptor
|
||||
|
||||
# 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
|
||||
alias_descriptor = desc_class(**descriptor_args)
|
||||
dct[alias_name.replace('-', '_')] = alias_descriptor
|
||||
return super(ConnectionMeta, meta).__new__(meta, name, bases, dct)
|
||||
|
||||
|
||||
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])
|
||||
return all_types
|
||||
|
||||
|
||||
def _find_service_description_class(service_type):
|
||||
package_name = 'openstack.{service_type}'.format(
|
||||
service_type=service_type).replace('-', '_')
|
||||
module_name = service_type.replace('-', '_') + '_service'
|
||||
class_name = ''.join(
|
||||
[part.capitalize() for part in module_name.split('_')])
|
||||
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(
|
||||
"Could not import {service_type} service description: {e}".format(
|
||||
service_type=service_type, e=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
|
131
openstack/_services_mixin.py
Normal file
131
openstack/_services_mixin.py
Normal file
@ -0,0 +1,131 @@
|
||||
# Generated file, to change, run tools/print-services.py
|
||||
from openstack import service_description
|
||||
from openstack.baremetal import baremetal_service
|
||||
from openstack.baremetal_introspection import baremetal_introspection_service
|
||||
from openstack.block_storage import block_storage_service
|
||||
from openstack.clustering import clustering_service
|
||||
from openstack.compute import compute_service
|
||||
from openstack.database import database_service
|
||||
from openstack.dns import dns_service
|
||||
from openstack.identity import identity_service
|
||||
from openstack.image import image_service
|
||||
from openstack.instance_ha import instance_ha_service
|
||||
from openstack.key_manager import key_manager_service
|
||||
from openstack.load_balancer import load_balancer_service
|
||||
from openstack.message import message_service
|
||||
from openstack.network import network_service
|
||||
from openstack.object_store import object_store_service
|
||||
from openstack.orchestration import orchestration_service
|
||||
from openstack.workflow import workflow_service
|
||||
|
||||
|
||||
class ServicesMixin(object):
|
||||
|
||||
identity = identity_service.IdentityService(service_type='identity')
|
||||
|
||||
compute = compute_service.ComputeService(service_type='compute')
|
||||
|
||||
image = image_service.ImageService(service_type='image')
|
||||
|
||||
load_balancer = load_balancer_service.LoadBalancerService(service_type='load-balancer')
|
||||
|
||||
object_store = object_store_service.ObjectStoreService(service_type='object-store')
|
||||
|
||||
clustering = clustering_service.ClusteringService(service_type='clustering')
|
||||
resource_cluster = clustering
|
||||
cluster = clustering
|
||||
|
||||
data_processing = service_description.ServiceDescription(service_type='data-processing')
|
||||
|
||||
baremetal = baremetal_service.BaremetalService(service_type='baremetal')
|
||||
bare_metal = baremetal
|
||||
|
||||
baremetal_introspection = baremetal_introspection_service.BaremetalIntrospectionService(service_type='baremetal-introspection')
|
||||
|
||||
key_manager = key_manager_service.KeyManagerService(service_type='key-manager')
|
||||
|
||||
resource_optimization = service_description.ServiceDescription(service_type='resource-optimization')
|
||||
infra_optim = resource_optimization
|
||||
|
||||
message = message_service.MessageService(service_type='message')
|
||||
messaging = message
|
||||
|
||||
application_catalog = service_description.ServiceDescription(service_type='application-catalog')
|
||||
|
||||
container_infrastructure_management = service_description.ServiceDescription(service_type='container-infrastructure-management')
|
||||
container_infrastructure = container_infrastructure_management
|
||||
container_infra = container_infrastructure_management
|
||||
|
||||
search = service_description.ServiceDescription(service_type='search')
|
||||
|
||||
dns = dns_service.DnsService(service_type='dns')
|
||||
|
||||
workflow = workflow_service.WorkflowService(service_type='workflow')
|
||||
|
||||
rating = service_description.ServiceDescription(service_type='rating')
|
||||
|
||||
operator_policy = service_description.ServiceDescription(service_type='operator-policy')
|
||||
policy = operator_policy
|
||||
|
||||
shared_file_system = service_description.ServiceDescription(service_type='shared-file-system')
|
||||
share = shared_file_system
|
||||
|
||||
data_protection_orchestration = service_description.ServiceDescription(service_type='data-protection-orchestration')
|
||||
|
||||
orchestration = orchestration_service.OrchestrationService(service_type='orchestration')
|
||||
|
||||
block_storage = block_storage_service.BlockStorageService(service_type='block-storage')
|
||||
block_store = block_storage
|
||||
volume = block_storage
|
||||
|
||||
alarm = service_description.ServiceDescription(service_type='alarm')
|
||||
alarming = alarm
|
||||
|
||||
meter = service_description.ServiceDescription(service_type='meter')
|
||||
metering = meter
|
||||
telemetry = meter
|
||||
|
||||
event = service_description.ServiceDescription(service_type='event')
|
||||
events = event
|
||||
|
||||
application_deployment = service_description.ServiceDescription(service_type='application-deployment')
|
||||
application_deployment = application_deployment
|
||||
|
||||
multi_region_network_automation = service_description.ServiceDescription(service_type='multi-region-network-automation')
|
||||
tricircle = multi_region_network_automation
|
||||
|
||||
database = database_service.DatabaseService(service_type='database')
|
||||
|
||||
application_container = service_description.ServiceDescription(service_type='application-container')
|
||||
container = application_container
|
||||
|
||||
root_cause_analysis = service_description.ServiceDescription(service_type='root-cause-analysis')
|
||||
rca = root_cause_analysis
|
||||
|
||||
nfv_orchestration = service_description.ServiceDescription(service_type='nfv-orchestration')
|
||||
|
||||
network = network_service.NetworkService(service_type='network')
|
||||
|
||||
backup = service_description.ServiceDescription(service_type='backup')
|
||||
|
||||
monitoring_logging = service_description.ServiceDescription(service_type='monitoring-logging')
|
||||
monitoring_log_api = monitoring_logging
|
||||
|
||||
monitoring = service_description.ServiceDescription(service_type='monitoring')
|
||||
|
||||
monitoring_events = service_description.ServiceDescription(service_type='monitoring-events')
|
||||
|
||||
placement = service_description.ServiceDescription(service_type='placement')
|
||||
|
||||
instance_ha = instance_ha_service.InstanceHaService(service_type='instance-ha')
|
||||
ha = instance_ha
|
||||
|
||||
reservation = service_description.ServiceDescription(service_type='reservation')
|
||||
|
||||
function_engine = service_description.ServiceDescription(service_type='function-engine')
|
||||
|
||||
accelerator = service_description.ServiceDescription(service_type='accelerator')
|
||||
|
||||
admin_logic = service_description.ServiceDescription(service_type='admin-logic')
|
||||
registration = admin_logic
|
||||
|
@ -183,7 +183,7 @@ import requestsexceptions
|
||||
import six
|
||||
|
||||
from openstack import _log
|
||||
from openstack._meta import connection as _meta
|
||||
from openstack import _services_mixin
|
||||
from openstack.cloud import openstackcloud as _cloud
|
||||
from openstack.cloud import _baremetal
|
||||
from openstack.cloud import _block_storage
|
||||
@ -245,23 +245,24 @@ def from_config(cloud=None, config=None, options=None, **kwargs):
|
||||
return Connection(config=config)
|
||||
|
||||
|
||||
class Connection(six.with_metaclass(_meta.ConnectionMeta,
|
||||
_cloud._OpenStackCloudMixin,
|
||||
_baremetal.BaremetalCloudMixin,
|
||||
_block_storage.BlockStorageCloudMixin,
|
||||
_compute.ComputeCloudMixin,
|
||||
_clustering.ClusteringCloudMixin,
|
||||
_coe.CoeCloudMixin,
|
||||
_dns.DnsCloudMixin,
|
||||
_floating_ip.FloatingIPCloudMixin,
|
||||
_identity.IdentityCloudMixin,
|
||||
_image.ImageCloudMixin,
|
||||
_network.NetworkCloudMixin,
|
||||
_network_common.NetworkCommonCloudMixin,
|
||||
_object_store.ObjectStoreCloudMixin,
|
||||
_orchestration.OrchestrationCloudMixin,
|
||||
_security_group.SecurityGroupCloudMixin
|
||||
)):
|
||||
class Connection(
|
||||
_services_mixin.ServicesMixin,
|
||||
_cloud._OpenStackCloudMixin,
|
||||
_baremetal.BaremetalCloudMixin,
|
||||
_block_storage.BlockStorageCloudMixin,
|
||||
_compute.ComputeCloudMixin,
|
||||
_clustering.ClusteringCloudMixin,
|
||||
_coe.CoeCloudMixin,
|
||||
_dns.DnsCloudMixin,
|
||||
_floating_ip.FloatingIPCloudMixin,
|
||||
_identity.IdentityCloudMixin,
|
||||
_image.ImageCloudMixin,
|
||||
_network.NetworkCloudMixin,
|
||||
_network_common.NetworkCommonCloudMixin,
|
||||
_object_store.ObjectStoreCloudMixin,
|
||||
_orchestration.OrchestrationCloudMixin,
|
||||
_security_group.SecurityGroupCloudMixin
|
||||
):
|
||||
|
||||
def __init__(self, cloud=None, config=None, session=None,
|
||||
app_name=None, app_version=None,
|
||||
|
128
tools/print-services.py
Normal file
128
tools/print-services.py
Normal file
@ -0,0 +1,128 @@
|
||||
# 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(
|
||||
'from {base_mod} import {dm}'.format(
|
||||
base_mod=base_mod,
|
||||
dm=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(
|
||||
'{alias_name} = {st}'.format(
|
||||
alias_name=alias_name,
|
||||
st=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(object):\n")
|
||||
for service in services:
|
||||
if service:
|
||||
print(" {service}".format(service=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 = 'openstack.{service_type}'.format(
|
||||
service_type=service_type).replace('-', '_')
|
||||
module_name = service_type.replace('-', '_') + '_service'
|
||||
class_name = ''.join(
|
||||
[part.capitalize() for part in module_name.split('_')])
|
||||
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(
|
||||
"Could not import {service_type} service description: {e}".format(
|
||||
service_type=service_type, e=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()
|
2
tox.ini
2
tox.ini
@ -101,7 +101,7 @@ commands = sphinx-build -a -E -W -d releasenotes/build/doctrees -b html releasen
|
||||
# breaks should occur before the binary operator for readability.
|
||||
ignore = H306,H4,W503
|
||||
show-source = True
|
||||
exclude=.venv,.git,.tox,dist,doc,*lib/python*,*egg,build
|
||||
exclude=.venv,.git,.tox,dist,doc,*lib/python*,*egg,build,openstack/_services_mixin.py
|
||||
|
||||
[doc8]
|
||||
extensions = .rst, .yaml
|
||||
|
Loading…
x
Reference in New Issue
Block a user