Add methods for getting Session and Client objects
These come originally from the shade library, but are helpful for things like the client libs themselves. Once one has a CloudConfig, there is really one and only one correct way to get both a session and a Client. Change-Id: I1b4d4321828864fddab85a127fbf63f4c8384ab9
This commit is contained in:
parent
796bfad22d
commit
a494b31b85
@ -14,6 +14,11 @@
|
||||
|
||||
import warnings
|
||||
|
||||
from keystoneauth1 import plugin
|
||||
from keystoneauth1 import session
|
||||
|
||||
from os_client_config import exceptions
|
||||
|
||||
|
||||
class CloudConfig(object):
|
||||
def __init__(self, name, region, config,
|
||||
@ -25,6 +30,7 @@ class CloudConfig(object):
|
||||
self._force_ipv4 = force_ipv4
|
||||
self._auth = auth_plugin
|
||||
self._openstack_config = openstack_config
|
||||
self._keystone_session = None
|
||||
|
||||
def __getattr__(self, key):
|
||||
"""Return arbitrary attributes."""
|
||||
@ -119,6 +125,139 @@ class CloudConfig(object):
|
||||
"""Return a keystoneauth plugin from the auth credentials."""
|
||||
return self._auth
|
||||
|
||||
def get_session(self):
|
||||
"""Return a keystoneauth session based on the auth credentials."""
|
||||
if self._keystone_session is None:
|
||||
if not self._auth:
|
||||
raise exceptions.OpenStackConfigException(
|
||||
"Problem with auth parameters")
|
||||
(verify, cert) = self.get_requests_verify_args()
|
||||
self._keystone_session = session.Session(
|
||||
auth=self._auth,
|
||||
verify=verify,
|
||||
cert=cert,
|
||||
timeout=self.config['api_timeout'])
|
||||
return self._keystone_session
|
||||
|
||||
def get_session_endpoint(self, service_key):
|
||||
"""Return the endpoint from config or the catalog.
|
||||
|
||||
If a configuration lists an explicit endpoint for a service,
|
||||
return that. Otherwise, fetch the service catalog from the
|
||||
keystone session and return the appropriate endpoint.
|
||||
|
||||
:param service_key: Generic key for service, such as 'compute' or
|
||||
'network'
|
||||
|
||||
:returns: Endpoint for the service, or None if not found
|
||||
"""
|
||||
|
||||
override_endpoint = self.get_endpoint(service_key)
|
||||
if override_endpoint:
|
||||
return override_endpoint
|
||||
# keystone is a special case in keystone, because what?
|
||||
session = self.get_session()
|
||||
if service_key == 'identity':
|
||||
endpoint = session.get_endpoint(interface=plugin.AUTH_INTERFACE)
|
||||
else:
|
||||
endpoint = session.get_endpoint(
|
||||
service_type=self.get_service_type(service_key),
|
||||
service_name=self.get_service_name(service_key),
|
||||
interface=self.get_interface(service_key),
|
||||
region_name=self.region)
|
||||
return endpoint
|
||||
|
||||
def get_legacy_client(
|
||||
self, service_key, client_class, interface_key=None,
|
||||
pass_version_arg=True, **kwargs):
|
||||
"""Return a legacy OpenStack client object for the given config.
|
||||
|
||||
Most of the OpenStack python-*client libraries have the same
|
||||
interface for their client constructors, but there are several
|
||||
parameters one wants to pass given a :class:`CloudConfig` object.
|
||||
|
||||
In the future, OpenStack API consumption should be done through
|
||||
the OpenStack SDK, but that's not ready yet. This is for getting
|
||||
Client objects from python-*client only.
|
||||
|
||||
:param service_key: Generic key for service, such as 'compute' or
|
||||
'network'
|
||||
:param client_class: Class of the client to be instantiated. This
|
||||
should be the unversioned version if there
|
||||
is one, such as novaclient.client.Client, or
|
||||
the versioned one, such as
|
||||
neutronclient.v2_0.client.Client if there isn't
|
||||
:param interface_key: (optional) Some clients, such as glanceclient
|
||||
only accept the parameter 'interface' instead
|
||||
of 'endpoint_type' - this is a get-out-of-jail
|
||||
parameter for those until they can be aligned.
|
||||
os-client-config understands this to be the
|
||||
case if service_key is image, so this is really
|
||||
only for use with other unknown broken clients.
|
||||
:param pass_version_arg: (optional) If a versioned Client constructor
|
||||
was passed to client_class, set this to
|
||||
False, which will tell get_client to not
|
||||
pass a version parameter. os-client-config
|
||||
already understand that this is the
|
||||
case for network, so it can be omitted in
|
||||
that case.
|
||||
:param kwargs: (optional) keyword args are passed through to the
|
||||
Client constructor, so this is in case anything
|
||||
additional needs to be passed in.
|
||||
"""
|
||||
# Because of course swift is different
|
||||
if service_key == 'object-store':
|
||||
return self._get_swift_client(client_class=client_class, **kwargs)
|
||||
interface = self.get_interface(service_key)
|
||||
# trigger exception on lack of service
|
||||
endpoint = self.get_session_endpoint(service_key)
|
||||
|
||||
if not interface_key:
|
||||
if service_key == 'image':
|
||||
interface_key = 'interface'
|
||||
else:
|
||||
interface_key = 'endpoint_type'
|
||||
if service_key == 'network':
|
||||
pass_version_arg = False
|
||||
|
||||
constructor_args = dict(
|
||||
session=self.get_session(),
|
||||
service_name=self.get_service_name(service_key),
|
||||
service_type=self.get_service_type(service_key),
|
||||
region_name=self.region)
|
||||
|
||||
if service_key == 'image':
|
||||
# os-client-config does not depend on glanceclient, but if
|
||||
# the user passed in glanceclient.client.Client, which they
|
||||
# would need to do if they were requesting 'image' - then
|
||||
# they necessarily have glanceclient installed
|
||||
from glanceclient.common import utils as glance_utils
|
||||
endpoint, version = glance_utils.strip_version(endpoint)
|
||||
constructor_args['endpoint'] = endpoint
|
||||
constructor_args.update(kwargs)
|
||||
constructor_args[interface_key] = interface
|
||||
if pass_version_arg:
|
||||
version = self.get_api_version(service_key)
|
||||
constructor_args['version'] = version
|
||||
return client_class(**constructor_args)
|
||||
|
||||
def _get_swift_client(self, client_class, **kwargs):
|
||||
session = self.get_session()
|
||||
token = session.get_token()
|
||||
endpoint = self.get_session_endpoint(service_key='object-store')
|
||||
if not endpoint:
|
||||
return None
|
||||
return client_class(
|
||||
preauthurl=endpoint,
|
||||
preauthtoken=token,
|
||||
auth_version=self.get_api_version('identity'),
|
||||
os_options=dict(
|
||||
auth_token=token,
|
||||
object_storage_url=endpoint,
|
||||
region_name=self.get_region_name()),
|
||||
timeout=self.api_timeout,
|
||||
)
|
||||
|
||||
def get_cache_expiration_time(self):
|
||||
if self._openstack_config:
|
||||
return self._openstack_config.get_cache_expiration_time()
|
||||
|
@ -12,7 +12,13 @@
|
||||
|
||||
import copy
|
||||
|
||||
from keystoneauth1 import plugin as ksa_plugin
|
||||
from keystoneauth1 import session as ksa_session
|
||||
import mock
|
||||
|
||||
from os_client_config import cloud_config
|
||||
from os_client_config import defaults
|
||||
from os_client_config import exceptions
|
||||
from os_client_config.tests import base
|
||||
|
||||
|
||||
@ -142,3 +148,132 @@ class TestCloudConfig(base.TestCase):
|
||||
cc.get_endpoint('image'))
|
||||
self.assertEqual(None, cc.get_service_name('compute'))
|
||||
self.assertEqual('locks', cc.get_service_name('identity'))
|
||||
|
||||
def test_get_session_no_auth(self):
|
||||
config_dict = defaults.get_defaults()
|
||||
config_dict.update(fake_services_dict)
|
||||
cc = cloud_config.CloudConfig("test1", "region-al", config_dict)
|
||||
self.assertRaises(
|
||||
exceptions.OpenStackConfigException,
|
||||
cc.get_session)
|
||||
|
||||
@mock.patch.object(ksa_session, 'Session')
|
||||
def test_get_session(self, mock_session):
|
||||
config_dict = defaults.get_defaults()
|
||||
config_dict.update(fake_services_dict)
|
||||
cc = cloud_config.CloudConfig(
|
||||
"test1", "region-al", config_dict, auth_plugin=mock.Mock())
|
||||
cc.get_session()
|
||||
mock_session.assert_called_with(
|
||||
auth=mock.ANY,
|
||||
verify=True, cert=None, timeout=None)
|
||||
|
||||
@mock.patch.object(ksa_session, 'Session')
|
||||
def test_override_session_endpoint(self, mock_session):
|
||||
config_dict = defaults.get_defaults()
|
||||
config_dict.update(fake_services_dict)
|
||||
cc = cloud_config.CloudConfig(
|
||||
"test1", "region-al", config_dict, auth_plugin=mock.Mock())
|
||||
self.assertEqual(
|
||||
cc.get_session_endpoint('compute'),
|
||||
fake_services_dict['compute_endpoint'])
|
||||
|
||||
@mock.patch.object(cloud_config.CloudConfig, 'get_session')
|
||||
def test_session_endpoint_identity(self, mock_get_session):
|
||||
mock_session = mock.Mock()
|
||||
mock_get_session.return_value = mock_session
|
||||
config_dict = defaults.get_defaults()
|
||||
config_dict.update(fake_services_dict)
|
||||
cc = cloud_config.CloudConfig(
|
||||
"test1", "region-al", config_dict, auth_plugin=mock.Mock())
|
||||
cc.get_session_endpoint('identity')
|
||||
mock_session.get_endpoint.assert_called_with(
|
||||
interface=ksa_plugin.AUTH_INTERFACE)
|
||||
|
||||
@mock.patch.object(cloud_config.CloudConfig, 'get_session')
|
||||
def test_session_endpoint(self, mock_get_session):
|
||||
mock_session = mock.Mock()
|
||||
mock_get_session.return_value = mock_session
|
||||
config_dict = defaults.get_defaults()
|
||||
config_dict.update(fake_services_dict)
|
||||
cc = cloud_config.CloudConfig(
|
||||
"test1", "region-al", config_dict, auth_plugin=mock.Mock())
|
||||
cc.get_session_endpoint('orchestration')
|
||||
mock_session.get_endpoint.assert_called_with(
|
||||
interface='public',
|
||||
service_name=None,
|
||||
region_name='region-al',
|
||||
service_type='orchestration')
|
||||
|
||||
@mock.patch.object(cloud_config.CloudConfig, 'get_session_endpoint')
|
||||
def test_legacy_client_object_store(self, mock_get_session_endpoint):
|
||||
mock_client = mock.Mock()
|
||||
mock_get_session_endpoint.return_value = 'http://example.com/v2'
|
||||
config_dict = defaults.get_defaults()
|
||||
config_dict.update(fake_services_dict)
|
||||
cc = cloud_config.CloudConfig(
|
||||
"test1", "region-al", config_dict, auth_plugin=mock.Mock())
|
||||
cc.get_legacy_client('object-store', mock_client)
|
||||
mock_client.assert_called_with(
|
||||
preauthtoken=mock.ANY,
|
||||
os_options={
|
||||
'auth_token': mock.ANY,
|
||||
'region_name': 'region-al',
|
||||
'object_storage_url': 'http://example.com/v2'
|
||||
},
|
||||
preauthurl='http://example.com/v2',
|
||||
auth_version='2.0',
|
||||
timeout=None)
|
||||
|
||||
@mock.patch.object(cloud_config.CloudConfig, 'get_session_endpoint')
|
||||
def test_legacy_client_image(self, mock_get_session_endpoint):
|
||||
mock_client = mock.Mock()
|
||||
mock_get_session_endpoint.return_value = 'http://example.com/v2'
|
||||
config_dict = defaults.get_defaults()
|
||||
config_dict.update(fake_services_dict)
|
||||
cc = cloud_config.CloudConfig(
|
||||
"test1", "region-al", config_dict, auth_plugin=mock.Mock())
|
||||
cc.get_legacy_client('image', mock_client)
|
||||
mock_client.assert_called_with(
|
||||
version='2',
|
||||
service_name=None,
|
||||
endpoint='http://example.com',
|
||||
region_name='region-al',
|
||||
interface='public',
|
||||
session=mock.ANY,
|
||||
# Not a typo - the config dict above overrides this
|
||||
service_type='mage'
|
||||
)
|
||||
|
||||
@mock.patch.object(cloud_config.CloudConfig, 'get_session_endpoint')
|
||||
def test_legacy_client_network(self, mock_get_session_endpoint):
|
||||
mock_client = mock.Mock()
|
||||
mock_get_session_endpoint.return_value = 'http://example.com/v2'
|
||||
config_dict = defaults.get_defaults()
|
||||
config_dict.update(fake_services_dict)
|
||||
cc = cloud_config.CloudConfig(
|
||||
"test1", "region-al", config_dict, auth_plugin=mock.Mock())
|
||||
cc.get_legacy_client('network', mock_client)
|
||||
mock_client.assert_called_with(
|
||||
endpoint_type='public',
|
||||
region_name='region-al',
|
||||
service_type='network',
|
||||
session=mock.ANY,
|
||||
service_name=None)
|
||||
|
||||
@mock.patch.object(cloud_config.CloudConfig, 'get_session_endpoint')
|
||||
def test_legacy_client_compute(self, mock_get_session_endpoint):
|
||||
mock_client = mock.Mock()
|
||||
mock_get_session_endpoint.return_value = 'http://example.com/v2'
|
||||
config_dict = defaults.get_defaults()
|
||||
config_dict.update(fake_services_dict)
|
||||
cc = cloud_config.CloudConfig(
|
||||
"test1", "region-al", config_dict, auth_plugin=mock.Mock())
|
||||
cc.get_legacy_client('compute', mock_client)
|
||||
mock_client.assert_called_with(
|
||||
version=2,
|
||||
endpoint_type='public',
|
||||
region_name='region-al',
|
||||
service_type='compute',
|
||||
session=mock.ANY,
|
||||
service_name=None)
|
||||
|
@ -9,6 +9,8 @@ extras
|
||||
fixtures>=0.3.14
|
||||
discover
|
||||
jsonschema>=2.0.0,<3.0.0,!=2.5.0
|
||||
mock>=1.2
|
||||
python-glanceclient>=0.18.0
|
||||
python-keystoneclient>=1.1.0
|
||||
python-subunit>=0.0.18
|
||||
sphinx>=1.1.2,!=1.2.0,!=1.3b1,<1.3
|
||||
|
Loading…
x
Reference in New Issue
Block a user