shade/shade/_legacy_clients.py

253 lines
11 KiB
Python

# 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 keystoneauth1 import plugin
from os_client_config import constructors
from shade import _utils
from shade import exc
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'.format(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_legacy_client('neutron', 'network')
@property
def nova_client(self):
return self._create_legacy_client('nova', 'compute', version='2.0')
@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')
@property
def keystone_client(self):
# Trigger discovery from ksa
self._identity_client
# Skip broken discovery in ksc. We're good thanks.
from keystoneclient.v2_0 import client as v2_client
from keystoneclient.v3 import client as v3_client
if self.cloud_config.config['identity_api_version'] == '3':
client_class = v3_client
else:
client_class = v2_client
return self._create_legacy_client(
'keystone', 'identity',
client_class=client_class.Client,
deprecated=True,
endpoint=self._identity_client.get_endpoint(),
endpoint_override=self._identity_client.get_endpoint())
# 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'
def _join_ksa_version(self, version):
return ".".join([str(x) for x in version])
@property
def ironic_client(self):
# Trigger discovery from ksa. This will make ironicclient and
# keystoneauth1.adapter.Adapter code paths both go through discovery.
# ironicclient does its own magic with discovery, so we won't
# pass an endpoint_override here like we do for keystoneclient.
# Just so it's not wasted though, make sure we can handle the
# min microversion we need.
needed = self._get_legacy_ironic_microversion()
# TODO(mordred) Bug in ksa - don't do microversion matching for
# auth_type = admin_token. Remove this if when the fix lands.
if (hasattr(plugin.BaseAuthPlugin, 'get_endpoint_data') or
self.cloud_config.config['auth_type'] not in (
'admin_token', 'none')):
# TODO(mordred) once we're on REST properly, we need a better
# method for matching requested and available microversion
endpoint_data = self._baremetal_client.get_endpoint_data()
if not endpoint_data.min_microversion:
raise exc.OpenStackCloudException(
"shade needs an ironic that supports microversions")
if endpoint_data.min_microversion[1] > int(needed[-1]):
raise exc.OpenStackCloudException(
"shade needs an ironic that supports microversion {needed}"
" but the ironic found has a minimum microversion"
" of {found}".format(
needed=needed,
found=self._join_ksa_version(
endpoint_data.min_microversion)))
if endpoint_data.max_microversion[1] < int(needed[-1]):
raise exc.OpenStackCloudException(
"shade needs an ironic that supports microversion {needed}"
" but the ironic found has a maximum microversion"
" of {found}".format(
needed=needed,
found=self._join_ksa_version(
endpoint_data.max_microversion)))
return self._create_legacy_client(
'ironic', 'baremetal',
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)