From 8483e1b1398d9230aef5f1a9d27720780f8b4340 Mon Sep 17 00:00:00 2001 From: Monty Taylor <mordred@inaugust.com> Date: Tue, 30 Jan 2018 08:22:47 -0600 Subject: [PATCH] Add OpenStackCloud object to Connection Make a cloud attribute on Connection so that people with a Connection can also use shade features. This changes the default for shade's list_flavors to NOT fetching extra_specs, which is very much yay. Change-Id: I45a5f7f11a9c5ab3c77443a8f5df26089243334c --- openstack/cloud/openstackcloud.py | 22 +++------- openstack/config/cloud_region.py | 20 +++++---- openstack/connection.py | 5 +++ .../tests/functional/cloud/test_flavor.py | 6 ++- openstack/tests/unit/cloud/test_caching.py | 6 --- openstack/tests/unit/cloud/test_flavors.py | 44 ++++++++++++++++++- 6 files changed, 69 insertions(+), 34 deletions(-) diff --git a/openstack/cloud/openstackcloud.py b/openstack/cloud/openstackcloud.py index c5dfe9d49..956514a04 100644 --- a/openstack/cloud/openstackcloud.py +++ b/openstack/cloud/openstackcloud.py @@ -50,7 +50,6 @@ from openstack.cloud import meta from openstack.cloud import _utils import openstack.config import openstack.config.defaults -import openstack.connection from openstack import task_manager from openstack import utils @@ -143,6 +142,7 @@ class OpenStackCloud(_normalize.Normalizer): app_name=None, app_version=None, use_direct_get=False, + conn=None, **kwargs): self.log = _log.setup_logging('openstack') @@ -162,12 +162,6 @@ class OpenStackCloud(_normalize.Normalizer): self.secgroup_source = cloud_config.config['secgroup_source'] self.force_ipv4 = cloud_config.force_ipv4 self.strict_mode = strict - # TODO(shade) The openstack.cloud default for get_flavor_extra_specs - # should be changed and this should be removed completely - self._extra_config = cloud_config._openstack_config.get_extra_config( - 'shade', { - 'get_flavor_extra_specs': True, - }) if manager is not None: self.manager = manager @@ -303,11 +297,14 @@ class OpenStackCloud(_normalize.Normalizer): _utils.localhost_supports_ipv6() if not self.force_ipv4 else False) self.cloud_config = cloud_config - self._conn_object = None + self._conn_object = conn @property def _conn(self): if not self._conn_object: + # Importing late to avoid import cycle. If the OpenStackCloud + # object comes via Connection, it'll have connection passed in. + import openstack.connection self._conn_object = openstack.connection.Connection( config=self.cloud_config, session=self._keystone_session) return self._conn_object @@ -1938,7 +1935,7 @@ class OpenStackCloud(_normalize.Normalizer): return ret @_utils.cache_on_arguments() - def list_flavors(self, get_extra=None): + def list_flavors(self, get_extra=False): """List all available flavors. :param get_extra: Whether or not to fetch extra specs for each flavor. @@ -1948,8 +1945,6 @@ class OpenStackCloud(_normalize.Normalizer): :returns: A list of flavor ``munch.Munch``. """ - if get_extra is None: - get_extra = self._extra_config['get_flavor_extra_specs'] data = _adapter._json_response( self._conn.compute.get( '/flavors/detail', params=dict(is_public='None')), @@ -2986,7 +2981,7 @@ class OpenStackCloud(_normalize.Normalizer): self.search_flavors, get_extra=get_extra) return _utils._get_entity(self, search_func, name_or_id, filters) - def get_flavor_by_id(self, id, get_extra=True): + def get_flavor_by_id(self, id, get_extra=False): """ Get a flavor by ID :param id: ID of the flavor. @@ -3002,9 +2997,6 @@ class OpenStackCloud(_normalize.Normalizer): flavor = self._normalize_flavor( self._get_and_munchify('flavor', data)) - if get_extra is None: - get_extra = self._extra_config['get_flavor_extra_specs'] - if not flavor.extra_specs and get_extra: endpoint = "/flavors/{id}/os-extra_specs".format( id=flavor.id) diff --git a/openstack/config/cloud_region.py b/openstack/config/cloud_region.py index 43042d558..422bee3bf 100644 --- a/openstack/config/cloud_region.py +++ b/openstack/config/cloud_region.py @@ -353,6 +353,7 @@ class CloudRegion(object): def get_cache_expiration_time(self): if self._openstack_config: return self._openstack_config.get_cache_expiration_time() + return 0 def get_cache_path(self): if self._openstack_config: @@ -361,6 +362,7 @@ class CloudRegion(object): def get_cache_class(self): if self._openstack_config: return self._openstack_config.get_cache_class() + return 'dogpile.cache.null' def get_cache_arguments(self): if self._openstack_config: @@ -400,56 +402,56 @@ class CloudRegion(object): def get_external_networks(self): """Get list of network names for external networks.""" return [ - net['name'] for net in self.config['networks'] + net['name'] for net in self.config.get('networks', []) if net['routes_externally']] def get_external_ipv4_networks(self): """Get list of network names for external IPv4 networks.""" return [ - net['name'] for net in self.config['networks'] + net['name'] for net in self.config.get('networks', []) if net['routes_ipv4_externally']] def get_external_ipv6_networks(self): """Get list of network names for external IPv6 networks.""" return [ - net['name'] for net in self.config['networks'] + net['name'] for net in self.config.get('networks', []) if net['routes_ipv6_externally']] def get_internal_networks(self): """Get list of network names for internal networks.""" return [ - net['name'] for net in self.config['networks'] + net['name'] for net in self.config.get('networks', []) if not net['routes_externally']] def get_internal_ipv4_networks(self): """Get list of network names for internal IPv4 networks.""" return [ - net['name'] for net in self.config['networks'] + net['name'] for net in self.config.get('networks', []) if not net['routes_ipv4_externally']] def get_internal_ipv6_networks(self): """Get list of network names for internal IPv6 networks.""" return [ - net['name'] for net in self.config['networks'] + net['name'] for net in self.config.get('networks', []) if not net['routes_ipv6_externally']] def get_default_network(self): """Get network used for default interactions.""" - for net in self.config['networks']: + for net in self.config.get('networks', []): if net['default_interface']: return net['name'] return None def get_nat_destination(self): """Get network used for NAT destination.""" - for net in self.config['networks']: + for net in self.config.get('networks', []): if net['nat_destination']: return net['name'] return None def get_nat_source(self): """Get network used for NAT source.""" - for net in self.config['networks']: + for net in self.config.get('networks', []): if net.get('nat_source'): return net['name'] return None diff --git a/openstack/connection.py b/openstack/connection.py index cc8bd33b4..a7cad826d 100644 --- a/openstack/connection.py +++ b/openstack/connection.py @@ -168,6 +168,7 @@ import six from openstack import _log from openstack import _meta +from openstack import cloud as _cloud from openstack import config as _config from openstack.config import cloud_region from openstack import exceptions @@ -311,6 +312,10 @@ class Connection(six.with_metaclass(_meta.ConnectionMeta)): self.session._sdk_connection = self self._proxies = {} + self.cloud = _cloud.OpenStackCloud( + cloud_config=self.config, + manager=self.task_manager, + conn=self) def add_service(self, service): """Add a service to the Connection. diff --git a/openstack/tests/functional/cloud/test_flavor.py b/openstack/tests/functional/cloud/test_flavor.py index 9bdcf8dd0..e4d4a7228 100644 --- a/openstack/tests/functional/cloud/test_flavor.py +++ b/openstack/tests/functional/cloud/test_flavor.py @@ -157,7 +157,8 @@ class TestFlavor(base.BaseFunctionalTestCase): # Now set them extra_specs = {'foo': 'aaa', 'bar': 'bbb'} self.operator_cloud.set_flavor_specs(new_flavor['id'], extra_specs) - mod_flavor = self.operator_cloud.get_flavor(new_flavor['id']) + mod_flavor = self.operator_cloud.get_flavor( + new_flavor['id'], get_extra=True) # Verify extra_specs were set self.assertIn('extra_specs', mod_flavor) @@ -165,7 +166,8 @@ class TestFlavor(base.BaseFunctionalTestCase): # Unset the 'foo' value self.operator_cloud.unset_flavor_specs(mod_flavor['id'], ['foo']) - mod_flavor = self.operator_cloud.get_flavor_by_id(new_flavor['id']) + mod_flavor = self.operator_cloud.get_flavor_by_id( + new_flavor['id'], get_extra=True) # Verify 'foo' is unset and 'bar' is still set self.assertEqual({'bar': 'bbb'}, mod_flavor['extra_specs']) diff --git a/openstack/tests/unit/cloud/test_caching.py b/openstack/tests/unit/cloud/test_caching.py index bcfc7cd63..b1bcc38d8 100644 --- a/openstack/tests/unit/cloud/test_caching.py +++ b/openstack/tests/unit/cloud/test_caching.py @@ -437,12 +437,6 @@ class TestMemoryCache(base.RequestsMockTestCase): dict(method='GET', uri=mock_uri, json={'flavors': fakes.FAKE_FLAVOR_LIST}) ] - uris_to_mock.extend([ - dict(method='GET', - uri='{endpoint}/flavors/{id}/os-extra_specs'.format( - endpoint=fakes.COMPUTE_ENDPOINT, id=flavor['id']), - json={'extra_specs': {}}) - for flavor in fakes.FAKE_FLAVOR_LIST]) self.register_uris(uris_to_mock) diff --git a/openstack/tests/unit/cloud/test_flavors.py b/openstack/tests/unit/cloud/test_flavors.py index e23e67b1f..0fa5ae877 100644 --- a/openstack/tests/unit/cloud/test_flavors.py +++ b/openstack/tests/unit/cloud/test_flavors.py @@ -82,6 +82,30 @@ class TestFlavors(base.RequestsMockTestCase): self.cloud.delete_flavor, 'vanilla') def test_list_flavors(self): + uris_to_mock = [ + dict(method='GET', + uri='{endpoint}/flavors/detail?is_public=None'.format( + endpoint=fakes.COMPUTE_ENDPOINT), + json={'flavors': fakes.FAKE_FLAVOR_LIST}), + ] + self.register_uris(uris_to_mock) + + flavors = self.cloud.list_flavors() + + # test that new flavor is created correctly + found = False + for flavor in flavors: + if flavor['name'] == 'vanilla': + found = True + break + self.assertTrue(found) + needed_keys = {'name', 'ram', 'vcpus', 'id', 'is_public', 'disk'} + if found: + # check flavor content + self.assertTrue(needed_keys.issubset(flavor.keys())) + self.assert_calls() + + def test_list_flavors_with_extra(self): uris_to_mock = [ dict(method='GET', uri='{endpoint}/flavors/detail?is_public=None'.format( @@ -96,7 +120,7 @@ class TestFlavors(base.RequestsMockTestCase): for flavor in fakes.FAKE_FLAVOR_LIST]) self.register_uris(uris_to_mock) - flavors = self.cloud.list_flavors() + flavors = self.cloud.list_flavors(get_extra=True) # test that new flavor is created correctly found = False @@ -238,6 +262,22 @@ class TestFlavors(base.RequestsMockTestCase): self.assert_calls() def test_get_flavor_by_id(self): + flavor_uri = '{endpoint}/flavors/1'.format( + endpoint=fakes.COMPUTE_ENDPOINT) + flavor_json = {'flavor': fakes.make_fake_flavor('1', 'vanilla')} + + self.register_uris([ + dict(method='GET', uri=flavor_uri, json=flavor_json), + ]) + + flavor1 = self.cloud.get_flavor_by_id('1') + self.assertEqual('1', flavor1['id']) + self.assertEqual({}, flavor1.extra_specs) + flavor2 = self.cloud.get_flavor_by_id('1') + self.assertEqual('1', flavor2['id']) + self.assertEqual({}, flavor2.extra_specs) + + def test_get_flavor_with_extra_specs(self): flavor_uri = '{endpoint}/flavors/1'.format( endpoint=fakes.COMPUTE_ENDPOINT) flavor_extra_uri = '{endpoint}/flavors/1/os-extra_specs'.format( @@ -250,7 +290,7 @@ class TestFlavors(base.RequestsMockTestCase): dict(method='GET', uri=flavor_extra_uri, json=flavor_extra_json), ]) - flavor1 = self.cloud.get_flavor_by_id('1') + flavor1 = self.cloud.get_flavor_by_id('1', get_extra=True) self.assertEqual('1', flavor1['id']) self.assertEqual({'name': 'test'}, flavor1.extra_specs) flavor2 = self.cloud.get_flavor_by_id('1', get_extra=False)