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
This commit is contained in:
Monty Taylor 2018-01-30 08:22:47 -06:00
parent aff2b6ab40
commit 8483e1b139
No known key found for this signature in database
GPG Key ID: 7BAE94BC7141A594
6 changed files with 69 additions and 34 deletions

View File

@ -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)

View File

@ -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

View File

@ -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.

View File

@ -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'])

View File

@ -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)

View File

@ -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)