Let os-client-config handle session creation

More things than shade need consistent Session creation, so we moved
a copy of the shade code into os-client-config to allow python-*client
to consume the same logic. Now that it's there, consume it in shade and
remove the need for shade to know/duplicate the logic.

Depends-On: Ide4c613cc143a0b8a3f36130989b57e808b2530f
Change-Id: I86cdb3cdd2710ef302520184ccfcb1605384f706
This commit is contained in:
Monty Taylor 2015-11-01 19:30:41 -05:00
parent 9df40b00dc
commit f7ca875777
6 changed files with 84 additions and 184 deletions

View File

@ -4,7 +4,7 @@ munch
decorator
jsonpatch
ipaddress
os-client-config>=1.9.0
os-client-config>=1.10.1
requestsexceptions>=1.1.1
six

View File

@ -26,15 +26,11 @@ import requestsexceptions
from cinderclient.v1 import client as cinder_client
import glanceclient
import glanceclient.exc
from glanceclient.common import utils as glance_utils
from heatclient import client as heat_client
from heatclient.common import template_utils
import keystoneauth1.exceptions
from keystoneauth1 import plugin as ksa_plugin
from keystoneauth1 import session as ksa_session
from keystoneclient.v2_0 import client as k2_client
from keystoneclient.v3 import client as k3_client
from neutronclient.v2_0 import client as neutron_client
from keystoneclient import client as keystone_client
from neutronclient.neutron import client as neutron_client
from novaclient import client as nova_client
from novaclient import exceptions as nova_exceptions
import swiftclient.client as swift_client
@ -209,8 +205,10 @@ class OpenStackCloud(object):
# If server expiration time is set explicitly, use that. Otherwise
# fall back to whatever it was before
self._SERVER_LIST_AGE = cache_expiration.get(
'server', self._SERVER_LIST_AGE)
# TODO(mordred) replace with get_cache_resource_expiration once
# it has a release with default value
self._SERVER_LIST_AGE = int(cache_expiration.get(
'server', self._SERVER_LIST_AGE))
self._container_cache = dict()
self._file_hash_cache = dict()
@ -250,23 +248,13 @@ class OpenStackCloud(object):
return generate_key
def _get_client(
self, service_key, client_class, interface_key='endpoint_type',
self, service_key, client_class, interface_key=None,
pass_version_arg=True, **kwargs):
try:
interface = self.cloud_config.get_interface(service_key)
# trigger exception on lack of service
self.get_session_endpoint(service_key)
constructor_args = dict(
session=self.keystone_session,
service_name=self.cloud_config.get_service_name(service_key),
service_type=self.cloud_config.get_service_type(service_key),
region_name=self.region_name)
constructor_args.update(kwargs)
constructor_args[interface_key] = interface
if pass_version_arg:
version = self.cloud_config.get_api_version(service_key)
constructor_args['version'] = version
client = client_class(**constructor_args)
client = self.cloud_config.get_legacy_client(
service_key=service_key, client_class=client_class,
interface_key=interface_key, pass_version_arg=pass_version_arg,
**kwargs)
except Exception:
self.log.debug(
"Couldn't construct {service} object".format(
@ -286,31 +274,11 @@ class OpenStackCloud(object):
'compute', nova_client.Client)
return self._nova_client
def _get_identity_client_class(self):
if self.cloud_config.get_api_version('identity') == '3':
return k3_client.Client
elif self.cloud_config.get_api_version('identity') in ('2', '2.0'):
return k2_client.Client
raise OpenStackCloudException(
"Unknown identity API version: {version}".format(
version=self.cloud_config.get_api_version('identity')))
@property
def keystone_session(self):
if self._keystone_session is None:
try:
keystone_auth = self.cloud_config.get_auth()
if not keystone_auth:
raise OpenStackCloudException(
"Problem with auth parameters")
self._keystone_session = ksa_session.Session(
auth=keystone_auth,
verify=self.verify,
cert=self.cert,
timeout=self.api_timeout)
except OpenStackCloudException:
raise
self._keystone_session = self.cloud_config.get_session()
except Exception as e:
raise OpenStackCloudException(
"Error authenticating to keystone: %s " % str(e))
@ -320,7 +288,7 @@ class OpenStackCloud(object):
def keystone_client(self):
if self._keystone_client is None:
self._keystone_client = self._get_client(
'identity', self._get_identity_client_class())
'identity', keystone_client.Client)
return self._keystone_client
@property
@ -614,13 +582,8 @@ class OpenStackCloud(object):
@property
def glance_client(self):
if self._glance_client is None:
endpoint, version = glance_utils.strip_version(
self.get_session_endpoint(service_key='image'))
# TODO(mordred): Put check detected vs. configured version
# and warn if they're different.
self._glance_client = self._get_client(
'image', glanceclient.Client, interface_key='interface',
endpoint=endpoint)
'image', glanceclient.Client)
return self._glance_client
@property
@ -644,25 +607,8 @@ class OpenStackCloud(object):
@property
def swift_client(self):
if self._swift_client is None:
try:
token = self.keystone_session.get_token()
endpoint = self.get_session_endpoint(
service_key='object-store')
self._swift_client = swift_client.Connection(
preauthurl=endpoint,
preauthtoken=token,
auth_version=self.cloud_config.get_api_version('identity'),
os_options=dict(
auth_token=token,
object_storage_url=endpoint,
region_name=self.region_name),
timeout=self.api_timeout,
)
except OpenStackCloudException:
raise
except Exception as e:
raise OpenStackCloudException(
"Error constructing swift client: %s", str(e))
self._swift_client = self._get_client(
'object-store', swift_client.Connection)
return self._swift_client
@property
@ -694,21 +640,6 @@ class OpenStackCloud(object):
@property
def trove_client(self):
if self._trove_client is None:
self.get_session_endpoint(service_key='database')
# Make the connection - can't use keystone session until there
# is one
self._trove_client = trove_client.Client(
self.cloud_config.get_api_version('database'),
session=self.keystone_session,
region_name=self.region_name,
service_type=self.cloud_config.get_service_type('database'),
)
if self._trove_client is None:
raise OpenStackCloudException(
"Failed to instantiate Trove client."
" This could mean that your credentials are wrong.")
self._trove_client = self._get_client(
'database', trove_client.Client)
return self._trove_client
@ -717,7 +648,7 @@ class OpenStackCloud(object):
def neutron_client(self):
if self._neutron_client is None:
self._neutron_client = self._get_client(
'network', neutron_client.Client, pass_version_arg=False)
'network', neutron_client.Client)
return self._neutron_client
def create_stack(
@ -808,22 +739,8 @@ class OpenStackCloud(object):
ram=ram, include=include))
def get_session_endpoint(self, service_key):
override_endpoint = self.cloud_config.get_endpoint(service_key)
if override_endpoint:
return override_endpoint
try:
# keystone is a special case in keystone, because what?
if service_key == 'identity':
endpoint = self.keystone_session.get_endpoint(
interface=ksa_plugin.AUTH_INTERFACE)
else:
endpoint = self.keystone_session.get_endpoint(
service_type=self.cloud_config.get_service_type(
service_key),
service_name=self.cloud_config.get_service_name(
service_key),
interface=self.cloud_config.get_interface(service_key),
region_name=self.region_name)
return self.cloud_config.get_session_endpoint(service_key)
except keystoneauth1.exceptions.catalog.EndpointNotFound as e:
self.log.debug(
"Endpoint not found in %s cloud: %s", self.name, str(e))
@ -843,7 +760,7 @@ class OpenStackCloud(object):
def has_service(self, service_key):
if not self.cloud_config.config.get('has_%s' % service_key, True):
self.log.debug(
"Overriding {service_key} entry in catalog per config".format(
"Disabling {service_key} entry in catalog per config".format(
service_key=service_key))
return False
try:

View File

@ -15,6 +15,7 @@
import mock
import os_client_config
from os_client_config import cloud_config
from swiftclient import client as swift_client
from swiftclient import service as swift_service
from swiftclient import exceptions as swift_exc
@ -35,15 +36,13 @@ class TestObject(base.TestCase):
cloud_config=config.get_one_cloud(validate=False))
@mock.patch.object(swift_client, 'Connection')
@mock.patch.object(shade.OpenStackCloud, 'keystone_session',
new_callable=mock.PropertyMock)
@mock.patch.object(shade.OpenStackCloud, 'get_session_endpoint')
def test_swift_client(self, endpoint_mock, session_mock, swift_mock):
endpoint_mock.return_value = 'danzig'
session = mock.MagicMock()
session.get_token = mock.MagicMock()
session.get_token.return_value = 'yankee'
session_mock.return_value = session
@mock.patch.object(cloud_config.CloudConfig, 'get_session')
def test_swift_client(self, get_session_mock, swift_mock):
session_mock = mock.Mock()
session_mock.get_endpoint.return_value = 'danzig'
session_mock.get_token.return_value = 'yankee'
get_session_mock.return_value = session_mock
self.cloud.swift_client
swift_mock.assert_called_with(
preauthurl='danzig',
@ -55,15 +54,15 @@ class TestObject(base.TestCase):
auth_token='yankee',
region_name=''))
@mock.patch.object(shade.OpenStackCloud, 'keystone_session',
new_callable=mock.PropertyMock)
@mock.patch.object(shade.OpenStackCloud, 'get_session_endpoint')
def test_swift_client_no_endpoint(self, endpoint_mock, session_mock):
endpoint_mock.side_effect = KeyError
@mock.patch.object(cloud_config.CloudConfig, 'get_session')
def test_swift_client_no_endpoint(self, get_session_mock):
session_mock = mock.Mock()
session_mock.get_endpoint.return_value = None
get_session_mock.return_value = session_mock
e = self.assertRaises(
exc.OpenStackCloudException, lambda: self.cloud.swift_client)
self.assertIn(
'Error constructing swift client', str(e))
'Failed to instantiate object-store client.', str(e))
@mock.patch.object(shade.OpenStackCloud, 'auth_token')
@mock.patch.object(shade.OpenStackCloud, 'get_session_endpoint')

View File

@ -15,6 +15,7 @@
import mock
import ironicclient
from os_client_config import cloud_config
import shade
from shade.tests import base
@ -35,12 +36,10 @@ class TestShadeOperatorNoAuth(base.TestCase):
validate=False,
)
@mock.patch.object(shade.OpenStackCloud, 'keystone_session',
new_callable=mock.PropertyMock)
@mock.patch.object(shade.OperatorCloud, 'get_session_endpoint')
@mock.patch.object(cloud_config.CloudConfig, 'get_session')
@mock.patch.object(ironicclient.client, 'Client')
def test_ironic_noauth_selection_using_a_task(
self, mock_client, mock_endpoint, session_mock):
self, mock_client, get_session_mock):
"""Test noauth selection for Ironic in OperatorCloud
Utilize a task to trigger the client connection attempt
@ -50,10 +49,11 @@ class TestShadeOperatorNoAuth(base.TestCase):
We want session_endpoint to be called because we're storing the
endpoint in a noauth token Session object now.
"""
session = mock.MagicMock()
session.get_token = mock.MagicMock()
session.get_token.return_value = 'yankee'
session_mock.return_value = session
session_mock = mock.Mock()
session_mock.get_endpoint.return_value = None
session_mock.get_token.return_value = 'yankee'
get_session_mock.return_value = session_mock
self.cloud_noauth.patch_machine('name', {})
self.assertTrue(mock_endpoint.called)
self.assertTrue(get_session_mock.called)
self.assertTrue(mock_client.called)

View File

@ -17,11 +17,9 @@ import munch
import glanceclient
from heatclient import client as heat_client
from keystoneclient.v2_0 import client as k2_client
from keystoneclient.v3 import client as k3_client
from neutronclient.common import exceptions as n_exc
import os_client_config.cloud_config
from os_client_config import cloud_config
import shade
from shade import exc
from shade import meta
@ -37,33 +35,6 @@ class TestShade(base.TestCase):
def test_openstack_cloud(self):
self.assertIsInstance(self.cloud, shade.OpenStackCloud)
@mock.patch.object(
os_client_config.cloud_config.CloudConfig, 'get_api_version')
def test_get_client_v2(self, mock_api_version):
mock_api_version.return_value = '2'
self.assertIs(
self.cloud._get_identity_client_class(),
k2_client.Client)
@mock.patch.object(
os_client_config.cloud_config.CloudConfig, 'get_api_version')
def test_get_client_v3(self, mock_api_version):
mock_api_version.return_value = '3'
self.assertIs(
self.cloud._get_identity_client_class(),
k3_client.Client)
@mock.patch.object(
os_client_config.cloud_config.CloudConfig, 'get_api_version')
def test_get_client_v4(self, mock_api_version):
mock_api_version.return_value = '4'
self.assertRaises(
exc.OpenStackCloudException,
self.cloud._get_identity_client_class)
@mock.patch.object(shade.OpenStackCloud, 'search_images')
def test_get_images(self, mock_search):
image1 = dict(id='123', name='mickey')
@ -98,33 +69,35 @@ class TestShade(base.TestCase):
self.assertRaises(exc.OpenStackCloudException,
self.cloud.list_servers)
@mock.patch.object(shade.OpenStackCloud, 'get_session_endpoint')
@mock.patch.object(shade.OpenStackCloud, 'keystone_session')
@mock.patch.object(cloud_config.CloudConfig, 'get_session')
@mock.patch.object(glanceclient, 'Client')
def test_glance_args(
self, mock_client, mock_keystone_session, mock_endpoint):
mock_keystone_session.return_value = None
mock_endpoint.return_value = 'http://example.com/v2'
self, mock_client, get_session_mock):
session_mock = mock.Mock()
session_mock.get_endpoint.return_value = 'http://example.com/v2'
get_session_mock.return_value = session_mock
self.cloud.glance_client
mock_client.assert_called_with(
'2',
endpoint='http://example.com',
version='2', region_name='', service_name=None,
region_name='', service_name=None,
interface='public',
service_type='image', session=mock.ANY,
)
@mock.patch.object(shade.OpenStackCloud, 'keystone_session')
@mock.patch.object(cloud_config.CloudConfig, 'get_session')
@mock.patch.object(heat_client, 'Client')
def test_heat_args(self, mock_client, mock_keystone_session):
mock_keystone_session.return_value = None
def test_heat_args(self, mock_client, get_session_mock):
session_mock = mock.Mock()
get_session_mock.return_value = session_mock
self.cloud.heat_client
mock_client.assert_called_with(
'1',
endpoint_type='public',
region_name='',
service_name=None,
service_type='orchestration',
session=mock.ANY,
version='1'
)
@mock.patch.object(shade.OpenStackCloud, 'search_subnets')

View File

@ -12,12 +12,12 @@
# License for the specific language governing permissions and limitations
# under the License.
from keystoneauth1 import plugin as ksc_plugin
from keystoneauth1 import plugin as ksa_plugin
import mock
import testtools
import os_client_config.cloud_config
from os_client_config import cloud_config
import shade
import munch
from shade import exc
@ -992,27 +992,30 @@ class TestShadeOperator(base.TestCase):
self.assertEqual('22', self.cloud.get_image_id('22'))
self.assertEqual('22', self.cloud.get_image_id('22 name'))
@mock.patch.object(
os_client_config.cloud_config.CloudConfig, 'get_endpoint')
@mock.patch.object(cloud_config.CloudConfig, 'get_endpoint')
def test_get_session_endpoint_provided(self, fake_get_endpoint):
fake_get_endpoint.return_value = 'http://fake.url'
self.assertEqual(
'http://fake.url', self.cloud.get_session_endpoint('image'))
@mock.patch.object(shade.OpenStackCloud, 'keystone_session')
def test_get_session_endpoint_session(self, session_mock):
@mock.patch.object(cloud_config.CloudConfig, 'get_session')
def test_get_session_endpoint_session(self, get_session_mock):
session_mock = mock.Mock()
session_mock.get_endpoint.return_value = 'http://fake.url'
get_session_mock.return_value = session_mock
self.assertEqual(
'http://fake.url', self.cloud.get_session_endpoint('image'))
@mock.patch.object(shade.OpenStackCloud, 'keystone_session')
def test_get_session_endpoint_exception(self, session_mock):
@mock.patch.object(cloud_config.CloudConfig, 'get_session')
def test_get_session_endpoint_exception(self, get_session_mock):
class FakeException(Exception):
pass
def side_effect(*args, **kwargs):
raise FakeException("No service")
session_mock = mock.Mock()
session_mock.get_endpoint.side_effect = side_effect
get_session_mock.return_value = session_mock
self.cloud.name = 'testcloud'
self.cloud.region_name = 'testregion'
with testtools.ExpectedException(
@ -1021,26 +1024,34 @@ class TestShadeOperator(base.TestCase):
" No service"):
self.cloud.get_session_endpoint("image")
@mock.patch.object(shade.OpenStackCloud, 'keystone_session')
def test_get_session_endpoint_unavailable(self, session_mock):
@mock.patch.object(cloud_config.CloudConfig, 'get_session')
def test_get_session_endpoint_unavailable(self, get_session_mock):
session_mock = mock.Mock()
session_mock.get_endpoint.return_value = None
get_session_mock.return_value = session_mock
image_endpoint = self.cloud.get_session_endpoint("image")
self.assertIsNone(image_endpoint)
@mock.patch.object(shade.OpenStackCloud, 'keystone_session')
def test_get_session_endpoint_identity(self, session_mock):
@mock.patch.object(cloud_config.CloudConfig, 'get_session')
def test_get_session_endpoint_identity(self, get_session_mock):
session_mock = mock.Mock()
get_session_mock.return_value = session_mock
self.cloud.get_session_endpoint('identity')
session_mock.get_endpoint.assert_called_with(
interface=ksc_plugin.AUTH_INTERFACE)
interface=ksa_plugin.AUTH_INTERFACE)
@mock.patch.object(shade.OpenStackCloud, 'keystone_session')
def test_has_service_no(self, session_mock):
@mock.patch.object(cloud_config.CloudConfig, 'get_session')
def test_has_service_no(self, get_session_mock):
session_mock = mock.Mock()
session_mock.get_endpoint.return_value = None
get_session_mock.return_value = session_mock
self.assertFalse(self.cloud.has_service("image"))
@mock.patch.object(shade.OpenStackCloud, 'keystone_session')
def test_has_service_yes(self, session_mock):
@mock.patch.object(cloud_config.CloudConfig, 'get_session')
def test_has_service_yes(self, get_session_mock):
session_mock = mock.Mock()
session_mock.get_endpoint.return_value = 'http://fake.url'
get_session_mock.return_value = session_mock
self.assertTrue(self.cloud.has_service("image"))
@mock.patch.object(shade._tasks.HypervisorList, 'main')