Move legacy client constructors to mixin

Remove them from the main class to make them slightly less obvious.

Change-Id: I72b32d992c71777155c22b51ce4c7868e19d44c1
This commit is contained in:
Monty Taylor 2017-05-12 10:58:46 -05:00
parent 103bc13862
commit 1c0a95ba8f
No known key found for this signature in database
GPG Key ID: 7BAE94BC7141A594
3 changed files with 202 additions and 167 deletions

197
shade/_legacy_clients.py Normal file
View File

@ -0,0 +1,197 @@
# 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
from os_client_config import constructors
from shade import _utils
class LegacyClientFactoryMixin(object):
"""Mixin Class containing factory functions for legacy client objects.
Methods in this class exist for backwards compatibility so will not go
away any time - but they are all things whose use is discouraged. They're
in a mixin to unclutter the main class file.
"""
def _create_legacy_client(
self, client, service, deprecated=True,
module_name=None, **kwargs):
if client not in self._legacy_clients:
if deprecated:
self._deprecated_import_check(client)
if module_name:
constructors.get_constructor_mapping()[service] = module_name
self._legacy_clients[client] = self._get_client(service, **kwargs)
return self._legacy_clients[client]
def _deprecated_import_check(self, client):
module_name = '{client}client'
warnings.warn(
'Using shade to get a {module_name} object is deprecated. If you'
' need a {module_name} object, please use make_legacy_client in'
' os-client-config instead'.format(module_name=module_name))
try:
importlib.import_module(module_name)
except ImportError:
self.log.error(
'{module_name} is no longer a dependency of shade. You need to'
' install python-{module_name} directly.'.format(
module_name=module_name))
raise
@property
def trove_client(self):
return self._create_legacy_client('trove', 'database')
@property
def magnum_client(self):
return self._create_legacy_client('magnum', 'container-infra')
@property
def neutron_client(self):
return self._create_or_return_legacy_client('neutron', 'network')
@property
def nova_client(self):
return self._create_legacy_client(
'nova', 'compute', version='2.0', deprecated=False)
@property
def glance_client(self):
return self._create_legacy_client('glance', 'image')
@property
def heat_client(self):
return self._create_legacy_client('heat', 'orchestration')
@property
def swift_client(self):
return self._create_legacy_client('swift', 'object-store')
@property
def cinder_client(self):
return self._create_legacy_client('cinder', 'volume')
@property
def designate_client(self):
return self._create_legacy_client('designate', 'dns', deprecated=False)
@property
def keystone_client(self):
return self._create_legacy_client(
'keystone', 'identity', deprecated=False)
# Set the ironic API microversion to a known-good
# supported/tested with the contents of shade.
#
# NOTE(TheJulia): Defaulted to version 1.6 as the ironic
# state machine changes which will increment the version
# and break an automatic transition of an enrolled node
# to an available state. Locking the version is intended
# to utilize the original transition until shade supports
# calling for node inspection to allow the transition to
# take place automatically.
# NOTE(mordred): shade will handle microversions more
# directly in the REST layer. This microversion property
# will never change. When we implement REST, we should
# start at 1.6 since that's what we've been requesting
# via ironic_client
@property
def ironic_api_microversion(self):
# NOTE(mordred) Abuse _legacy_clients to only show
# this warning once
if 'ironic-microversion' not in self._legacy_clients:
warnings.warn(
'shade is transitioning to direct REST calls which'
' will handle microversions with no action needed'
' on the part of the user. The ironic_api_microversion'
' property is only used by the legacy ironic_client'
' constructor and will never change. If you are using'
' it for any reason, either switch to just using'
' shade ironic-related API calls, or use os-client-config'
' make_legacy_client directly and pass os_ironic_api_version'
' to it as an argument. It is highly recommended to'
' stop using this property.')
self._legacy_clients['ironic-microversion'] = True
return self._get_legacy_ironic_microversion()
def _get_legacy_ironic_microversion(self):
return '1.6'
@property
def ironic_client(self):
return self._create_legacy_client(
'ironic', 'baremetal', deprecated=False,
module_name='ironicclient.client.Client',
os_ironic_api_version=self._get_legacy_ironic_microversion())
def _get_swift_kwargs(self):
auth_version = self.cloud_config.get_api_version('identity')
auth_args = self.cloud_config.config.get('auth', {})
os_options = {'auth_version': auth_version}
if auth_version == '2.0':
os_options['os_tenant_name'] = auth_args.get('project_name')
os_options['os_tenant_id'] = auth_args.get('project_id')
else:
os_options['os_project_name'] = auth_args.get('project_name')
os_options['os_project_id'] = auth_args.get('project_id')
for key in (
'username',
'password',
'auth_url',
'user_id',
'project_domain_id',
'project_domain_name',
'user_domain_id',
'user_domain_name'):
os_options['os_{key}'.format(key=key)] = auth_args.get(key)
return os_options
@property
def swift_service(self):
suppress_warning = 'swift-service' not in self._legacy_clients
return self.make_swift_service_object(suppress_warning)
def make_swift_service(self, suppress_warning=False):
# NOTE(mordred): Not using helper functions because the
# error message needs to be different
if not suppress_warning:
warnings.warn(
'Using shade to get a SwiftService object is deprecated. shade'
' will automatically do the things SwiftServices does as part'
' of the normal object resource calls. If you are having'
' trouble using those such that you still need to use'
' SwiftService, please file a bug with shade.'
' If you understand the issues and want to make this warning'
' go away, use cloud.make_swift_service(True) instead of'
' cloud.swift_service')
# Abuse self._legacy_clients so that we only give the warning
# once. We don't cache SwiftService objects.
self._legacy_clients['swift-service'] = True
try:
import swiftclient.service
except ImportError:
self.log.error(
'swiftclient is no longer a dependency of shade. You need to'
' install python-swiftclient directly.')
with _utils.shade_exceptions("Error constructing SwiftService"):
endpoint = self.get_session_endpoint(
service_key='object-store')
options = dict(os_auth_token=self.auth_token,
os_storage_url=endpoint,
os_region_name=self.region_name)
options.update(self._get_swift_kwargs())
return swiftclient.service.SwiftService(options=options)

View File

@ -13,7 +13,6 @@
import collections
import functools
import hashlib
import importlib
import ipaddress
import json
import jsonpatch
@ -40,6 +39,7 @@ from shade import _adapter
from shade._heat import event_utils
from shade._heat import template_utils
from shade import _log
from shade import _legacy_clients
from shade import _normalize
from shade import meta
from shade import task_manager
@ -92,7 +92,9 @@ def _no_pending_stacks(stacks):
return True
class OpenStackCloud(_normalize.Normalizer):
class OpenStackCloud(
_normalize.Normalizer,
_legacy_clients.LegacyClientFactoryMixin):
"""Represent a connection to an OpenStack Cloud.
OpenStackCloud is the entry point for all cloud operations, regardless
@ -296,18 +298,7 @@ class OpenStackCloud(_normalize.Normalizer):
self._keystone_session = None
self._cinder_client = None
self._glance_client = None
self._glance_endpoint = None
self._heat_client = None
self._keystone_client = None
self._neutron_client = None
self._nova_client = None
self._trove_client = None
self._designate_client = None
self._magnum_client = None
self._swift_client = None
self._legacy_clients = {}
self._raw_clients = {}
self._local_ipv6 = _utils.localhost_supports_ipv6()
@ -543,134 +534,6 @@ class OpenStackCloud(_normalize.Normalizer):
new_resource = _utils._dictify_resource(resource)
return pprint.pformat(new_resource)
# LEGACY CLIENT IMPORTS
def _deprecated_import_check(self, module_name):
warnings.warn(
'Using shade to get a {module_name} object is deprecated. If you'
' need a {module_name} object, please use make_legacy_client in'
' os-client-config instead'.format(module_name=module_name))
try:
importlib.import_module(module_name)
except ImportError:
self.log.error(
'{module_name} is no longer a dependency of shade. You need to'
' install python-{module_name} directly.'.format(
module_name=module_name))
raise
@property
def trove_client(self):
if self._trove_client is None:
_deprecated_import_check('troveclient')
self._trove_client = self._get_client('database')
return self._trove_client
@property
def magnum_client(self):
if self._magnum_client is None:
_deprecated_import_check('magnumclient')
self._magnum_client = self._get_client('container-infra')
return self._magnum_client
@property
def neutron_client(self):
if self._neutron_client is None:
_deprecated_import_check('neutronclient')
self._neutron_client = self._get_client('network')
return self._neutron_client
@property
def nova_client(self):
if self._nova_client is None:
self._nova_client = self._get_client('compute', version='2.0')
return self._nova_client
@property
def glance_client(self):
if self._glance_client is None:
_deprecated_import_check('glanceclient')
self._glance_client = self._get_client('image')
return self._glance_client
@property
def heat_client(self):
if self._heat_client is None:
_deprecated_import_check('heatclient')
self._heat_client = self._get_client('orchestration')
return self._heat_client
@property
def swift_client(self):
if self._swift_client is None:
_deprecated_import_check('swiftclient')
self._swift_client = self._get_client('object-store')
return self._swift_client
def _get_swift_kwargs(self):
auth_version = self.cloud_config.get_api_version('identity')
auth_args = self.cloud_config.config.get('auth', {})
os_options = {'auth_version': auth_version}
if auth_version == '2.0':
os_options['os_tenant_name'] = auth_args.get('project_name')
os_options['os_tenant_id'] = auth_args.get('project_id')
else:
os_options['os_project_name'] = auth_args.get('project_name')
os_options['os_project_id'] = auth_args.get('project_id')
for key in (
'username',
'password',
'auth_url',
'user_id',
'project_domain_id',
'project_domain_name',
'user_domain_id',
'user_domain_name'):
os_options['os_{key}'.format(key=key)] = auth_args.get(key)
return os_options
@property
def swift_service(self):
# NOTE(mordred): Not using deprecated_client_check because the
# error message needs to be different
try:
import swiftclient.service
except ImportError:
self.log.error(
'swiftclient is no longer a dependency of shade. You need to'
' install python-swiftclient directly.')
with _utils.shade_exceptions("Error constructing "
"swift client"):
endpoint = self.get_session_endpoint(
service_key='object-store')
options = dict(os_auth_token=self.auth_token,
os_storage_url=endpoint,
os_region_name=self.region_name)
options.update(self._get_swift_kwargs())
return swiftclient.service.SwiftService(options=options)
@property
def cinder_client(self):
if self._cinder_client is None:
_deprecated_import_check('cinderclient')
self._cinder_client = self._get_client('volume')
return self._cinder_client
@property
def designate_client(self):
# Note: Explicit constructor is needed until occ 1.27.0
import designateclient.client # flake8: noqa
if self._designate_client is None:
self._designate_client = self._get_client(
'dns', designateclient.client.Client)
return self._designate_client
@property
def keystone_client(self):
if self._keystone_client is None:
self._keystone_client = self._get_client('identity')
return self._keystone_client
@property
def keystone_session(self):
if self._keystone_session is None:

View File

@ -13,7 +13,6 @@
import datetime
import jsonpatch
from ironicclient import client as ironic_client
from ironicclient import exceptions as ironic_exceptions
from novaclient import exceptions as nova_exceptions
@ -32,30 +31,6 @@ class OperatorCloud(openstackcloud.OpenStackCloud):
See the :class:`OpenStackCloud` class for a description of most options.
"""
def __init__(self, *args, **kwargs):
super(OperatorCloud, self).__init__(*args, **kwargs)
self._ironic_client = None
# Set the ironic API microversion to a known-good
# supported/tested with the contents of shade.
#
# Note(TheJulia): Defaulted to version 1.6 as the ironic
# state machine changes which will increment the version
# and break an automatic transition of an enrolled node
# to an available state. Locking the version is intended
# to utilize the original transition until shade supports
# calling for node inspection to allow the transition to
# take place automatically.
ironic_api_microversion = '1.6'
@property
def ironic_client(self):
if self._ironic_client is None:
self._ironic_client = self._get_client(
'baremetal', ironic_client.Client,
os_ironic_api_version=self.ironic_api_microversion)
return self._ironic_client
def list_nics(self):
with _utils.shade_exceptions("Error fetching machine port list"):
return self.manager.submit_task(_tasks.MachinePortList())