298 lines
11 KiB
Python
298 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 requests.exceptions
|
|
import uuid
|
|
|
|
from keystoneauth1 import exceptions as ks_exc
|
|
|
|
from openstack.config import cloud_region
|
|
from openstack import connection
|
|
from openstack import exceptions
|
|
from openstack.tests import fakes
|
|
from openstack.tests.unit import base
|
|
|
|
|
|
class TestFromConf(base.TestCase):
|
|
|
|
def _get_conn(self, **from_conf_kwargs):
|
|
oslocfg = self._load_ks_cfg_opts()
|
|
# Throw name in here to prove **kwargs is working
|
|
config = cloud_region.from_conf(
|
|
oslocfg, session=self.cloud.session, name='from_conf.example.com',
|
|
**from_conf_kwargs)
|
|
self.assertEqual('from_conf.example.com', config.name)
|
|
|
|
return connection.Connection(config=config, strict_proxies=True)
|
|
|
|
def test_adapter_opts_set(self):
|
|
"""Adapter opts specified in the conf."""
|
|
conn = self._get_conn()
|
|
|
|
discovery = {
|
|
"versions": {
|
|
"values": [
|
|
{"status": "stable",
|
|
"updated": "2019-06-01T00:00:00Z",
|
|
"media-types": [{
|
|
"base": "application/json",
|
|
"type": "application/vnd.openstack.heat-v2+json"}],
|
|
"id": "v2.0",
|
|
"links": [{
|
|
"href": "https://example.org:8888/heat/v2",
|
|
"rel": "self"}]
|
|
}]
|
|
}
|
|
}
|
|
self.register_uris([
|
|
dict(method='GET',
|
|
uri='https://example.org:8888/heat/v2',
|
|
json=discovery),
|
|
dict(method='GET',
|
|
uri='https://example.org:8888/heat/v2/foo',
|
|
json={'foo': {}}),
|
|
])
|
|
|
|
adap = conn.orchestration
|
|
self.assertEqual('SpecialRegion', adap.region_name)
|
|
self.assertEqual('orchestration', adap.service_type)
|
|
self.assertEqual('internal', adap.interface)
|
|
self.assertEqual('https://example.org:8888/heat/v2',
|
|
adap.endpoint_override)
|
|
|
|
adap.get('/foo')
|
|
self.assert_calls()
|
|
|
|
def test_default_adapter_opts(self):
|
|
"""Adapter opts are registered, but all defaulting in conf."""
|
|
conn = self._get_conn()
|
|
|
|
server_id = str(uuid.uuid4())
|
|
server_name = self.getUniqueString('name')
|
|
fake_server = fakes.make_fake_server(server_id, server_name)
|
|
|
|
self.register_uris([
|
|
self.get_nova_discovery_mock_dict(),
|
|
dict(method='GET',
|
|
uri=self.get_mock_url(
|
|
'compute', 'public', append=['servers', 'detail']),
|
|
json={'servers': [fake_server]}),
|
|
])
|
|
|
|
# Nova has empty adapter config, so these default
|
|
adap = conn.compute
|
|
self.assertIsNone(adap.region_name)
|
|
self.assertEqual('compute', adap.service_type)
|
|
self.assertEqual('public', adap.interface)
|
|
self.assertIsNone(adap.endpoint_override)
|
|
|
|
s = next(adap.servers())
|
|
self.assertEqual(s.id, server_id)
|
|
self.assertEqual(s.name, server_name)
|
|
self.assert_calls()
|
|
|
|
def test_service_not_ready_catalog(self):
|
|
"""Adapter opts are registered, but all defaulting in conf."""
|
|
conn = self._get_conn()
|
|
|
|
server_id = str(uuid.uuid4())
|
|
server_name = self.getUniqueString('name')
|
|
fake_server = fakes.make_fake_server(server_id, server_name)
|
|
|
|
self.register_uris([
|
|
dict(method='GET',
|
|
uri='https://compute.example.com/v2.1/',
|
|
exc=requests.exceptions.ConnectionError),
|
|
self.get_nova_discovery_mock_dict(),
|
|
dict(method='GET',
|
|
uri=self.get_mock_url(
|
|
'compute', 'public', append=['servers', 'detail']),
|
|
json={'servers': [fake_server]}),
|
|
])
|
|
|
|
self.assertRaises(
|
|
exceptions.ServiceDiscoveryException,
|
|
getattr, conn, 'compute')
|
|
|
|
# Nova has empty adapter config, so these default
|
|
adap = conn.compute
|
|
self.assertIsNone(adap.region_name)
|
|
self.assertEqual('compute', adap.service_type)
|
|
self.assertEqual('public', adap.interface)
|
|
self.assertIsNone(adap.endpoint_override)
|
|
|
|
s = next(adap.servers())
|
|
self.assertEqual(s.id, server_id)
|
|
self.assertEqual(s.name, server_name)
|
|
self.assert_calls()
|
|
|
|
def test_name_with_dashes(self):
|
|
conn = self._get_conn()
|
|
|
|
discovery = {
|
|
"versions": {
|
|
"values": [
|
|
{"status": "stable",
|
|
"id": "v1",
|
|
"links": [{
|
|
"href": "https://example.org:5050/v1",
|
|
"rel": "self"}]
|
|
}]
|
|
}
|
|
}
|
|
status = {
|
|
'finished': True,
|
|
'error': None
|
|
}
|
|
self.register_uris([
|
|
dict(method='GET',
|
|
uri='https://example.org:5050',
|
|
json=discovery),
|
|
# strict-proxies means we're going to fetch the discovery
|
|
# doc from the versioned endpoint to verify it works.
|
|
dict(method='GET',
|
|
uri='https://example.org:5050/v1',
|
|
json=discovery),
|
|
dict(method='GET',
|
|
uri='https://example.org:5050/v1/introspection/abcd',
|
|
json=status),
|
|
])
|
|
|
|
adap = conn.baremetal_introspection
|
|
self.assertEqual('baremetal-introspection', adap.service_type)
|
|
self.assertEqual('public', adap.interface)
|
|
self.assertEqual('https://example.org:5050/v1', adap.endpoint_override)
|
|
|
|
self.assertTrue(adap.get_introspection('abcd').is_finished)
|
|
|
|
def test_service_not_ready_endpoint_override(self):
|
|
conn = self._get_conn()
|
|
|
|
discovery = {
|
|
"versions": {
|
|
"values": [
|
|
{"status": "stable",
|
|
"id": "v1",
|
|
"links": [{
|
|
"href": "https://example.org:5050/v1",
|
|
"rel": "self"}]
|
|
}]
|
|
}
|
|
}
|
|
status = {
|
|
'finished': True,
|
|
'error': None
|
|
}
|
|
self.register_uris([
|
|
dict(method='GET',
|
|
uri='https://example.org:5050',
|
|
exc=requests.exceptions.ConnectTimeout),
|
|
dict(method='GET',
|
|
uri='https://example.org:5050',
|
|
json=discovery),
|
|
# strict-proxies means we're going to fetch the discovery
|
|
# doc from the versioned endpoint to verify it works.
|
|
dict(method='GET',
|
|
uri='https://example.org:5050/v1',
|
|
json=discovery),
|
|
dict(method='GET',
|
|
uri='https://example.org:5050/v1/introspection/abcd',
|
|
json=status),
|
|
])
|
|
|
|
self.assertRaises(
|
|
exceptions.ServiceDiscoveryException,
|
|
getattr, conn, 'baremetal_introspection')
|
|
|
|
adap = conn.baremetal_introspection
|
|
self.assertEqual('baremetal-introspection', adap.service_type)
|
|
self.assertEqual('public', adap.interface)
|
|
self.assertEqual('https://example.org:5050/v1', adap.endpoint_override)
|
|
|
|
self.assertTrue(adap.get_introspection('abcd').is_finished)
|
|
|
|
def assert_service_disabled(self, service_type, expected_reason,
|
|
**from_conf_kwargs):
|
|
conn = self._get_conn(**from_conf_kwargs)
|
|
# The _ServiceDisabledProxyShim loads up okay...
|
|
adap = getattr(conn, service_type)
|
|
# ...but freaks out if you try to use it.
|
|
ex = self.assertRaises(
|
|
exceptions.ServiceDisabledException, getattr, adap, 'get')
|
|
self.assertIn("Service '%s' is disabled because its configuration "
|
|
"could not be loaded." % service_type, ex.message)
|
|
self.assertIn(expected_reason, ex.message)
|
|
|
|
def test_no_such_conf_section(self):
|
|
"""No conf section (therefore no adapter opts) for service type."""
|
|
del self.oslo_config_dict['heat']
|
|
self.assert_service_disabled(
|
|
'orchestration',
|
|
"No section for project 'heat' (service type 'orchestration') was "
|
|
"present in the config.")
|
|
|
|
def test_no_such_conf_section_ignore_service_type(self):
|
|
"""Ignore absent conf section if service type not requested."""
|
|
del self.oslo_config_dict['heat']
|
|
self.assert_service_disabled(
|
|
'orchestration', "Not in the list of requested service_types.",
|
|
# 'orchestration' absent from this list
|
|
service_types=['compute'])
|
|
|
|
def test_no_adapter_opts(self):
|
|
"""Conf section present, but opts for service type not registered."""
|
|
self.oslo_config_dict['heat'] = None
|
|
self.assert_service_disabled(
|
|
'orchestration',
|
|
"Encountered an exception attempting to process config for "
|
|
"project 'heat' (service type 'orchestration'): no such option")
|
|
|
|
def test_no_adapter_opts_ignore_service_type(self):
|
|
"""Ignore unregistered conf section if service type not requested."""
|
|
self.oslo_config_dict['heat'] = None
|
|
self.assert_service_disabled(
|
|
'orchestration', "Not in the list of requested service_types.",
|
|
# 'orchestration' absent from this list
|
|
service_types=['compute'])
|
|
|
|
def test_invalid_adapter_opts(self):
|
|
"""Adapter opts are bogus, in exception-raising ways."""
|
|
self.oslo_config_dict['heat'] = {
|
|
'interface': 'public',
|
|
'valid_interfaces': 'private',
|
|
}
|
|
self.assert_service_disabled(
|
|
'orchestration',
|
|
"Encountered an exception attempting to process config for "
|
|
"project 'heat' (service type 'orchestration'): interface and "
|
|
"valid_interfaces are mutually exclusive.")
|
|
|
|
def test_no_session(self):
|
|
# TODO(efried): Currently calling without a Session is not implemented.
|
|
self.assertRaises(exceptions.ConfigException,
|
|
cloud_region.from_conf, self._load_ks_cfg_opts())
|
|
|
|
def test_no_endpoint(self):
|
|
"""Conf contains adapter opts, but service type not in catalog."""
|
|
self.os_fixture.v3_token.remove_service('monitoring')
|
|
conn = self._get_conn()
|
|
# Monasca is not in the service catalog
|
|
self.assertRaises(ks_exc.catalog.EndpointNotFound,
|
|
getattr, conn, 'monitoring')
|
|
|
|
def test_no_endpoint_ignore_service_type(self):
|
|
"""Bogus service type disabled if not in requested service_types."""
|
|
self.assert_service_disabled(
|
|
'monitoring', "Not in the list of requested service_types.",
|
|
# 'monitoring' absent from this list
|
|
service_types={'compute', 'orchestration', 'bogus'})
|