Switch flavor ops in the cloud layer to proxy
Since very long time we want to switch cloud layer functions to use underneath the proxy layer to reduce maintenance complexity. For flavors we even did some tries in the past. This time it is meant even more seriosly. Since flavors listing is a subject of caching, but dogpile/pickle does not support caching of the complex objects returned by funciton we convert and return flavors to Munch objects (we anyway wanted to have it this way). Change-Id: I0353bb8d1be69e18dd31f0abedf25818b42c14ce
This commit is contained in:
parent
256e25e321
commit
292c917949
@ -159,29 +159,13 @@ class ComputeCloudMixin(_normalize.Normalizer):
|
||||
:returns: A list of flavor ``munch.Munch``.
|
||||
|
||||
"""
|
||||
data = proxy._json_response(
|
||||
self.compute.get(
|
||||
'/flavors/detail', params=dict(is_public='None')),
|
||||
error_message="Error fetching flavor list")
|
||||
flavors = self._normalize_flavors(
|
||||
self._get_and_munchify('flavors', data))
|
||||
data = self.compute.flavors(details=True)
|
||||
flavors = []
|
||||
|
||||
for flavor in flavors:
|
||||
for flavor in data:
|
||||
if not flavor.extra_specs and get_extra:
|
||||
endpoint = "/flavors/{id}/os-extra_specs".format(
|
||||
id=flavor.id)
|
||||
try:
|
||||
data = proxy._json_response(
|
||||
self.compute.get(endpoint),
|
||||
error_message="Error fetching flavor extra specs")
|
||||
flavor.extra_specs = self._get_and_munchify(
|
||||
'extra_specs', data)
|
||||
except exc.OpenStackCloudHTTPError as e:
|
||||
flavor.extra_specs = {}
|
||||
self.log.debug(
|
||||
'Fetching extra specs for flavor failed:'
|
||||
' %(msg)s', {'msg': str(e)})
|
||||
|
||||
flavor.fetch_extra_specs(self.compute)
|
||||
flavors.append(flavor._to_munch(original_names=False))
|
||||
return flavors
|
||||
|
||||
def list_server_security_groups(self, server):
|
||||
@ -441,9 +425,12 @@ class ComputeCloudMixin(_normalize.Normalizer):
|
||||
found.
|
||||
|
||||
"""
|
||||
search_func = functools.partial(
|
||||
self.search_flavors, get_extra=get_extra)
|
||||
return _utils._get_entity(self, search_func, name_or_id, filters)
|
||||
if not filters:
|
||||
filters = {}
|
||||
flavor = self.compute.find_flavor(
|
||||
name_or_id, get_extra_specs=get_extra, **filters)
|
||||
if flavor:
|
||||
return flavor._to_munch(original_names=False)
|
||||
|
||||
def get_flavor_by_id(self, id, get_extra=False):
|
||||
""" Get a flavor by ID
|
||||
@ -454,29 +441,8 @@ class ComputeCloudMixin(_normalize.Normalizer):
|
||||
specs.
|
||||
:returns: A flavor ``munch.Munch``.
|
||||
"""
|
||||
data = proxy._json_response(
|
||||
self.compute.get('/flavors/{id}'.format(id=id)),
|
||||
error_message="Error getting flavor with ID {id}".format(id=id)
|
||||
)
|
||||
flavor = self._normalize_flavor(
|
||||
self._get_and_munchify('flavor', data))
|
||||
|
||||
if not flavor.extra_specs and get_extra:
|
||||
endpoint = "/flavors/{id}/os-extra_specs".format(
|
||||
id=flavor.id)
|
||||
try:
|
||||
data = proxy._json_response(
|
||||
self.compute.get(endpoint),
|
||||
error_message="Error fetching flavor extra specs")
|
||||
flavor.extra_specs = self._get_and_munchify(
|
||||
'extra_specs', data)
|
||||
except exc.OpenStackCloudHTTPError as e:
|
||||
flavor.extra_specs = {}
|
||||
self.log.debug(
|
||||
'Fetching extra specs for flavor failed:'
|
||||
' %(msg)s', {'msg': str(e)})
|
||||
|
||||
return flavor
|
||||
flavor = self.compute.get_flavor(id, get_extra_specs=get_extra)
|
||||
return flavor._to_munch(original_names=False)
|
||||
|
||||
def get_server_console(self, server, length=None):
|
||||
"""Get the console log for a server.
|
||||
@ -1412,27 +1378,23 @@ class ComputeCloudMixin(_normalize.Normalizer):
|
||||
|
||||
:raises: OpenStackCloudException on operation error.
|
||||
"""
|
||||
with _utils.shade_exceptions("Failed to create flavor {name}".format(
|
||||
name=name)):
|
||||
payload = {
|
||||
'disk': disk,
|
||||
'OS-FLV-EXT-DATA:ephemeral': ephemeral,
|
||||
'id': flavorid,
|
||||
'os-flavor-access:is_public': is_public,
|
||||
'name': name,
|
||||
'ram': ram,
|
||||
'rxtx_factor': rxtx_factor,
|
||||
'swap': swap,
|
||||
'vcpus': vcpus,
|
||||
}
|
||||
if flavorid == 'auto':
|
||||
payload['id'] = None
|
||||
data = proxy._json_response(self.compute.post(
|
||||
'/flavors',
|
||||
json=dict(flavor=payload)))
|
||||
attrs = {
|
||||
'disk': disk,
|
||||
'ephemeral': ephemeral,
|
||||
'id': flavorid,
|
||||
'is_public': is_public,
|
||||
'name': name,
|
||||
'ram': ram,
|
||||
'rxtx_factor': rxtx_factor,
|
||||
'swap': swap,
|
||||
'vcpus': vcpus,
|
||||
}
|
||||
if flavorid == 'auto':
|
||||
attrs['id'] = None
|
||||
|
||||
return self._normalize_flavor(
|
||||
self._get_and_munchify('flavor', data))
|
||||
flavor = self.compute.create_flavor(**attrs)
|
||||
|
||||
return flavor._to_munch(original_names=False)
|
||||
|
||||
def delete_flavor(self, name_or_id):
|
||||
"""Delete a flavor
|
||||
@ -1443,19 +1405,17 @@ class ComputeCloudMixin(_normalize.Normalizer):
|
||||
|
||||
:raises: OpenStackCloudException on operation error.
|
||||
"""
|
||||
flavor = self.get_flavor(name_or_id, get_extra=False)
|
||||
if flavor is None:
|
||||
self.log.debug(
|
||||
"Flavor %s not found for deleting", name_or_id)
|
||||
return False
|
||||
|
||||
proxy._json_response(
|
||||
self.compute.delete(
|
||||
'/flavors/{id}'.format(id=flavor['id'])),
|
||||
error_message="Unable to delete flavor {name}".format(
|
||||
name=name_or_id))
|
||||
|
||||
return True
|
||||
try:
|
||||
flavor = self.compute.find_flavor(name_or_id)
|
||||
if not flavor:
|
||||
self.log.debug(
|
||||
"Flavor %s not found for deleting", name_or_id)
|
||||
return False
|
||||
self.compute.delete_flavor(flavor)
|
||||
return True
|
||||
except exceptions.SDKException:
|
||||
raise exceptions.OpenStackCloudException(
|
||||
"Unable to delete flavor {name}".format(name=name_or_id))
|
||||
|
||||
def set_flavor_specs(self, flavor_id, extra_specs):
|
||||
"""Add extra specs to a flavor
|
||||
@ -1466,11 +1426,7 @@ class ComputeCloudMixin(_normalize.Normalizer):
|
||||
:raises: OpenStackCloudException on operation error.
|
||||
:raises: OpenStackCloudResourceNotFound if flavor ID is not found.
|
||||
"""
|
||||
proxy._json_response(
|
||||
self.compute.post(
|
||||
"/flavors/{id}/os-extra_specs".format(id=flavor_id),
|
||||
json=dict(extra_specs=extra_specs)),
|
||||
error_message="Unable to set flavor specs")
|
||||
self.compute.create_flavor_extra_specs(flavor_id, extra_specs)
|
||||
|
||||
def unset_flavor_specs(self, flavor_id, keys):
|
||||
"""Delete extra specs from a flavor
|
||||
@ -1482,24 +1438,7 @@ class ComputeCloudMixin(_normalize.Normalizer):
|
||||
:raises: OpenStackCloudResourceNotFound if flavor ID is not found.
|
||||
"""
|
||||
for key in keys:
|
||||
proxy._json_response(
|
||||
self.compute.delete(
|
||||
"/flavors/{id}/os-extra_specs/{key}".format(
|
||||
id=flavor_id, key=key)),
|
||||
error_message="Unable to delete flavor spec {0}".format(key))
|
||||
|
||||
def _mod_flavor_access(self, action, flavor_id, project_id):
|
||||
"""Common method for adding and removing flavor access
|
||||
"""
|
||||
with _utils.shade_exceptions("Error trying to {action} access from "
|
||||
"flavor ID {flavor}".format(
|
||||
action=action, flavor=flavor_id)):
|
||||
endpoint = '/flavors/{id}/action'.format(id=flavor_id)
|
||||
access = {'tenant': project_id}
|
||||
access_key = '{action}TenantAccess'.format(action=action)
|
||||
|
||||
proxy._json_response(
|
||||
self.compute.post(endpoint, json={access_key: access}))
|
||||
self.compute.delete_flavor_extra_specs_property(flavor_id, key)
|
||||
|
||||
def add_flavor_access(self, flavor_id, project_id):
|
||||
"""Grant access to a private flavor for a project/tenant.
|
||||
@ -1509,7 +1448,7 @@ class ComputeCloudMixin(_normalize.Normalizer):
|
||||
|
||||
:raises: OpenStackCloudException on operation error.
|
||||
"""
|
||||
self._mod_flavor_access('add', flavor_id, project_id)
|
||||
self.compute.flavor_add_tenant_access(flavor_id, project_id)
|
||||
|
||||
def remove_flavor_access(self, flavor_id, project_id):
|
||||
"""Revoke access from a private flavor for a project/tenant.
|
||||
@ -1519,7 +1458,7 @@ class ComputeCloudMixin(_normalize.Normalizer):
|
||||
|
||||
:raises: OpenStackCloudException on operation error.
|
||||
"""
|
||||
self._mod_flavor_access('remove', flavor_id, project_id)
|
||||
self.compute.flavor_remove_tenant_access(flavor_id, project_id)
|
||||
|
||||
def list_flavor_access(self, flavor_id):
|
||||
"""List access from a private flavor for a project/tenant.
|
||||
@ -1530,14 +1469,8 @@ class ComputeCloudMixin(_normalize.Normalizer):
|
||||
|
||||
:raises: OpenStackCloudException on operation error.
|
||||
"""
|
||||
data = proxy._json_response(
|
||||
self.compute.get(
|
||||
'/flavors/{id}/os-flavor-access'.format(id=flavor_id)),
|
||||
error_message=(
|
||||
"Error trying to list access from flavorID {flavor}".format(
|
||||
flavor=flavor_id)))
|
||||
return _utils.normalize_flavor_accesses(
|
||||
self._get_and_munchify('flavor_access', data))
|
||||
access = self.compute.get_flavor_access(flavor_id)
|
||||
return _utils.normalize_flavor_accesses(access)
|
||||
|
||||
def list_hypervisors(self, filters={}):
|
||||
"""List all hypervisors
|
||||
|
@ -62,7 +62,7 @@ class Proxy(proxy.Proxy):
|
||||
# ========== Flavors ==========
|
||||
|
||||
def find_flavor(self, name_or_id, ignore_missing=True,
|
||||
get_extra_specs=False):
|
||||
get_extra_specs=False, **query):
|
||||
"""Find a single flavor
|
||||
|
||||
:param name_or_id: The name or ID of a flavor.
|
||||
@ -73,10 +73,14 @@ class Proxy(proxy.Proxy):
|
||||
:param bool get_extra_specs: When set to ``True`` and extra_specs not
|
||||
present in the response will invoke additional API call to fetch
|
||||
extra_specs.
|
||||
|
||||
:param kwargs query: Optional query parameters to be sent to limit
|
||||
the flavors being returned.
|
||||
|
||||
:returns: One :class:`~openstack.compute.v2.flavor.Flavor` or None
|
||||
"""
|
||||
flavor = self._find(_flavor.Flavor, name_or_id,
|
||||
ignore_missing=ignore_missing)
|
||||
flavor = self._find(
|
||||
_flavor.Flavor, name_or_id, ignore_missing=ignore_missing, **query)
|
||||
if flavor and get_extra_specs and not flavor.extra_specs:
|
||||
flavor = flavor.fetch_extra_specs(self)
|
||||
return flavor
|
||||
@ -126,19 +130,25 @@ class Proxy(proxy.Proxy):
|
||||
flavor = flavor.fetch_extra_specs(self)
|
||||
return flavor
|
||||
|
||||
def flavors(self, details=True, **query):
|
||||
def flavors(self, details=True, get_extra_specs=False, **query):
|
||||
"""Return a generator of flavors
|
||||
|
||||
:param bool details: When ``True``, returns
|
||||
:class:`~openstack.compute.v2.flavor.Flavor` objects,
|
||||
with additional attributes filled.
|
||||
:param bool get_extra_specs: When set to ``True`` and extra_specs not
|
||||
present in the response will invoke additional API call to fetch
|
||||
extra_specs.
|
||||
:param kwargs query: Optional query parameters to be sent to limit
|
||||
the flavors being returned.
|
||||
|
||||
:returns: A generator of flavor objects
|
||||
"""
|
||||
base_path = '/flavors/detail' if details else '/flavors'
|
||||
return self._list(_flavor.Flavor, base_path=base_path, **query)
|
||||
for flv in self._list(_flavor.Flavor, base_path=base_path, **query):
|
||||
if get_extra_specs and not flv.extra_specs:
|
||||
flv = flv.fetch_extra_specs(self)
|
||||
yield flv
|
||||
|
||||
def flavor_add_tenant_access(self, flavor, tenant):
|
||||
"""Adds tenant/project access to flavor.
|
||||
|
@ -62,7 +62,7 @@ class Flavor(resource.Resource):
|
||||
# TODO(mordred) extra_specs can historically also come from
|
||||
# OS-FLV-WITH-EXT-SPECS:extra_specs. Do we care?
|
||||
#: A dictionary of the flavor's extra-specs key-and-value pairs.
|
||||
extra_specs = resource.Body('extra_specs', type=dict)
|
||||
extra_specs = resource.Body('extra_specs', type=dict, default={})
|
||||
|
||||
@classmethod
|
||||
def list(cls, session, paginated=True, base_path='/flavors/detail',
|
||||
|
@ -66,10 +66,8 @@ class TestFlavor(base.BaseFunctionalTest):
|
||||
|
||||
# We should also always have ephemeral and public attributes
|
||||
self.assertIn('ephemeral', flavor)
|
||||
self.assertIn('OS-FLV-EXT-DATA:ephemeral', flavor)
|
||||
self.assertEqual(5, flavor['ephemeral'])
|
||||
self.assertIn('is_public', flavor)
|
||||
self.assertIn('os-flavor-access:is_public', flavor)
|
||||
self.assertTrue(flavor['is_public'])
|
||||
|
||||
for key in flavor_kwargs.keys():
|
||||
|
@ -18,6 +18,7 @@ from testscenarios import load_tests_apply_scenarios as load_tests # noqa
|
||||
import openstack
|
||||
import openstack.cloud
|
||||
from openstack.cloud import meta
|
||||
from openstack.compute.v2 import flavor as _flavor
|
||||
from openstack import exceptions
|
||||
from openstack.tests import fakes
|
||||
from openstack.tests.unit import base
|
||||
@ -436,10 +437,16 @@ class TestMemoryCache(base.TestCase):
|
||||
endpoint=fakes.COMPUTE_ENDPOINT)
|
||||
|
||||
uris_to_mock = [
|
||||
dict(method='GET', uri=mock_uri, json={'flavors': []}),
|
||||
dict(method='GET', uri=mock_uri,
|
||||
validate=dict(
|
||||
headers={'OpenStack-API-Version': 'compute 2.53'}),
|
||||
json={'flavors': []}),
|
||||
dict(method='GET', uri=mock_uri,
|
||||
validate=dict(
|
||||
headers={'OpenStack-API-Version': 'compute 2.53'}),
|
||||
json={'flavors': fakes.FAKE_FLAVOR_LIST})
|
||||
]
|
||||
self.use_compute_discovery()
|
||||
|
||||
self.register_uris(uris_to_mock)
|
||||
|
||||
@ -447,8 +454,11 @@ class TestMemoryCache(base.TestCase):
|
||||
|
||||
self.assertEqual([], self.cloud.list_flavors())
|
||||
|
||||
fake_flavor_dicts = self.cloud._normalize_flavors(
|
||||
fakes.FAKE_FLAVOR_LIST)
|
||||
fake_flavor_dicts = [
|
||||
_flavor.Flavor(connection=self.cloud, **f)
|
||||
for f in fakes.FAKE_FLAVOR_LIST
|
||||
]
|
||||
|
||||
self.cloud.list_flavors.invalidate(self.cloud)
|
||||
self.assertEqual(fake_flavor_dicts, self.cloud.list_flavors())
|
||||
|
||||
|
@ -791,11 +791,12 @@ class TestCreateServer(base.TestCase):
|
||||
dict(method='GET',
|
||||
uri='https://image.example.com/v2/images',
|
||||
json=fake_image_search_return),
|
||||
self.get_nova_discovery_mock_dict(),
|
||||
dict(method='GET',
|
||||
uri=self.get_mock_url(
|
||||
'compute', 'public', append=['flavors', 'detail'],
|
||||
qs_elements=['is_public=None']),
|
||||
json={'flavors': fakes.FAKE_FLAVOR_LIST}),
|
||||
'compute', 'public', append=['flavors', 'vanilla'],
|
||||
qs_elements=[]),
|
||||
json=fakes.FAKE_FLAVOR),
|
||||
dict(method='POST',
|
||||
uri=self.get_mock_url(
|
||||
'compute', 'public', append=['servers']),
|
||||
|
@ -18,8 +18,13 @@ from openstack.tests.unit import base
|
||||
|
||||
class TestFlavors(base.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
super(TestFlavors, self).setUp()
|
||||
# self.use_compute_discovery()
|
||||
|
||||
def test_create_flavor(self):
|
||||
|
||||
self.use_compute_discovery()
|
||||
self.register_uris([
|
||||
dict(method='POST',
|
||||
uri='{endpoint}/flavors'.format(
|
||||
@ -44,11 +49,12 @@ class TestFlavors(base.TestCase):
|
||||
self.assert_calls()
|
||||
|
||||
def test_delete_flavor(self):
|
||||
self.use_compute_discovery()
|
||||
self.register_uris([
|
||||
dict(method='GET',
|
||||
uri='{endpoint}/flavors/detail?is_public=None'.format(
|
||||
uri='{endpoint}/flavors/vanilla'.format(
|
||||
endpoint=fakes.COMPUTE_ENDPOINT),
|
||||
json={'flavors': fakes.FAKE_FLAVOR_LIST}),
|
||||
json=fakes.FAKE_FLAVOR),
|
||||
dict(method='DELETE',
|
||||
uri='{endpoint}/flavors/{id}'.format(
|
||||
endpoint=fakes.COMPUTE_ENDPOINT, id=fakes.FLAVOR_ID))])
|
||||
@ -57,7 +63,12 @@ class TestFlavors(base.TestCase):
|
||||
self.assert_calls()
|
||||
|
||||
def test_delete_flavor_not_found(self):
|
||||
self.use_compute_discovery()
|
||||
self.register_uris([
|
||||
dict(method='GET',
|
||||
uri='{endpoint}/flavors/invalid'.format(
|
||||
endpoint=fakes.COMPUTE_ENDPOINT),
|
||||
status_code=404),
|
||||
dict(method='GET',
|
||||
uri='{endpoint}/flavors/detail?is_public=None'.format(
|
||||
endpoint=fakes.COMPUTE_ENDPOINT),
|
||||
@ -68,7 +79,12 @@ class TestFlavors(base.TestCase):
|
||||
self.assert_calls()
|
||||
|
||||
def test_delete_flavor_exception(self):
|
||||
self.use_compute_discovery()
|
||||
self.register_uris([
|
||||
dict(method='GET',
|
||||
uri='{endpoint}/flavors/vanilla'.format(
|
||||
endpoint=fakes.COMPUTE_ENDPOINT),
|
||||
json=fakes.FAKE_FLAVOR),
|
||||
dict(method='GET',
|
||||
uri='{endpoint}/flavors/detail?is_public=None'.format(
|
||||
endpoint=fakes.COMPUTE_ENDPOINT),
|
||||
@ -82,6 +98,7 @@ class TestFlavors(base.TestCase):
|
||||
self.cloud.delete_flavor, 'vanilla')
|
||||
|
||||
def test_list_flavors(self):
|
||||
self.use_compute_discovery()
|
||||
uris_to_mock = [
|
||||
dict(method='GET',
|
||||
uri='{endpoint}/flavors/detail?is_public=None'.format(
|
||||
@ -106,6 +123,7 @@ class TestFlavors(base.TestCase):
|
||||
self.assert_calls()
|
||||
|
||||
def test_list_flavors_with_extra(self):
|
||||
self.use_compute_discovery()
|
||||
uris_to_mock = [
|
||||
dict(method='GET',
|
||||
uri='{endpoint}/flavors/detail?is_public=None'.format(
|
||||
@ -136,6 +154,7 @@ class TestFlavors(base.TestCase):
|
||||
self.assert_calls()
|
||||
|
||||
def test_get_flavor_by_ram(self):
|
||||
self.use_compute_discovery()
|
||||
uris_to_mock = [
|
||||
dict(method='GET',
|
||||
uri='{endpoint}/flavors/detail?is_public=None'.format(
|
||||
@ -154,6 +173,7 @@ class TestFlavors(base.TestCase):
|
||||
self.assertEqual(fakes.STRAWBERRY_FLAVOR_ID, flavor['id'])
|
||||
|
||||
def test_get_flavor_by_ram_and_include(self):
|
||||
self.use_compute_discovery()
|
||||
uris_to_mock = [
|
||||
dict(method='GET',
|
||||
uri='{endpoint}/flavors/detail?is_public=None'.format(
|
||||
@ -171,6 +191,7 @@ class TestFlavors(base.TestCase):
|
||||
self.assertEqual(fakes.STRAWBERRY_FLAVOR_ID, flavor['id'])
|
||||
|
||||
def test_get_flavor_by_ram_not_found(self):
|
||||
self.use_compute_discovery()
|
||||
self.register_uris([
|
||||
dict(method='GET',
|
||||
uri='{endpoint}/flavors/detail?is_public=None'.format(
|
||||
@ -182,19 +203,19 @@ class TestFlavors(base.TestCase):
|
||||
ram=100)
|
||||
|
||||
def test_get_flavor_string_and_int(self):
|
||||
flavor_list_uri = '{endpoint}/flavors/detail?is_public=None'.format(
|
||||
endpoint=fakes.COMPUTE_ENDPOINT)
|
||||
self.use_compute_discovery()
|
||||
flavor_resource_uri = '{endpoint}/flavors/1/os-extra_specs'.format(
|
||||
endpoint=fakes.COMPUTE_ENDPOINT)
|
||||
flavor_list_json = {'flavors': [fakes.make_fake_flavor(
|
||||
'1', 'vanilla')]}
|
||||
flavor = fakes.make_fake_flavor('1', 'vanilla')
|
||||
flavor_json = {'extra_specs': {}}
|
||||
|
||||
self.register_uris([
|
||||
dict(method='GET', uri=flavor_list_uri, json=flavor_list_json),
|
||||
dict(method='GET',
|
||||
uri='{endpoint}/flavors/1'.format(
|
||||
endpoint=fakes.COMPUTE_ENDPOINT),
|
||||
json=flavor),
|
||||
dict(method='GET', uri=flavor_resource_uri, json=flavor_json),
|
||||
dict(method='GET', uri=flavor_list_uri, json=flavor_list_json),
|
||||
dict(method='GET', uri=flavor_resource_uri, json=flavor_json)])
|
||||
])
|
||||
|
||||
flavor1 = self.cloud.get_flavor('1')
|
||||
self.assertEqual('1', flavor1['id'])
|
||||
@ -202,6 +223,7 @@ class TestFlavors(base.TestCase):
|
||||
self.assertEqual('1', flavor2['id'])
|
||||
|
||||
def test_set_flavor_specs(self):
|
||||
self.use_compute_discovery()
|
||||
extra_specs = dict(key1='value1')
|
||||
self.register_uris([
|
||||
dict(method='POST',
|
||||
@ -213,6 +235,7 @@ class TestFlavors(base.TestCase):
|
||||
self.assert_calls()
|
||||
|
||||
def test_unset_flavor_specs(self):
|
||||
self.use_compute_discovery()
|
||||
keys = ['key1', 'key2']
|
||||
self.register_uris([
|
||||
dict(method='DELETE',
|
||||
@ -262,6 +285,7 @@ class TestFlavors(base.TestCase):
|
||||
self.assert_calls()
|
||||
|
||||
def test_get_flavor_by_id(self):
|
||||
self.use_compute_discovery()
|
||||
flavor_uri = '{endpoint}/flavors/1'.format(
|
||||
endpoint=fakes.COMPUTE_ENDPOINT)
|
||||
flavor_json = {'flavor': fakes.make_fake_flavor('1', 'vanilla')}
|
||||
@ -278,6 +302,7 @@ class TestFlavors(base.TestCase):
|
||||
self.assertEqual({}, flavor2.extra_specs)
|
||||
|
||||
def test_get_flavor_with_extra_specs(self):
|
||||
self.use_compute_discovery()
|
||||
flavor_uri = '{endpoint}/flavors/1'.format(
|
||||
endpoint=fakes.COMPUTE_ENDPOINT)
|
||||
flavor_extra_uri = '{endpoint}/flavors/1/os-extra_specs'.format(
|
||||
|
@ -47,6 +47,13 @@ class TestFlavor(TestComputeProxy):
|
||||
def test_flavor_find(self):
|
||||
self.verify_find(self.proxy.find_flavor, flavor.Flavor)
|
||||
|
||||
def test_flavor_find_query(self):
|
||||
self.verify_find(
|
||||
self.proxy.find_flavor, flavor.Flavor,
|
||||
method_kwargs={"a": "b"},
|
||||
expected_kwargs={"a": "b", "ignore_missing": False}
|
||||
)
|
||||
|
||||
def test_flavor_find_fetch_extra(self):
|
||||
"""fetch extra_specs is triggered"""
|
||||
with mock.patch(
|
||||
@ -129,17 +136,56 @@ class TestFlavor(TestComputeProxy):
|
||||
)
|
||||
mocked.assert_not_called()
|
||||
|
||||
def test_flavors_detailed(self):
|
||||
self.verify_list(self.proxy.flavors, flavor.FlavorDetail,
|
||||
method_kwargs={"details": True, "query": 1},
|
||||
expected_kwargs={"query": 1,
|
||||
"base_path": "/flavors/detail"})
|
||||
@mock.patch("openstack.proxy.Proxy._list", auto_spec=True)
|
||||
@mock.patch("openstack.compute.v2.flavor.Flavor.fetch_extra_specs",
|
||||
auto_spec=True)
|
||||
def test_flavors_detailed(self, fetch_mock, list_mock):
|
||||
res = self.proxy.flavors(details=True)
|
||||
for r in res:
|
||||
self.assertIsNotNone(r)
|
||||
fetch_mock.assert_not_called()
|
||||
list_mock.assert_called_with(
|
||||
flavor.Flavor,
|
||||
base_path="/flavors/detail"
|
||||
)
|
||||
|
||||
def test_flavors_not_detailed(self):
|
||||
self.verify_list(self.proxy.flavors, flavor.Flavor,
|
||||
method_kwargs={"details": False, "query": 1},
|
||||
expected_kwargs={"query": 1,
|
||||
"base_path": "/flavors"})
|
||||
@mock.patch("openstack.proxy.Proxy._list", auto_spec=True)
|
||||
@mock.patch("openstack.compute.v2.flavor.Flavor.fetch_extra_specs",
|
||||
auto_spec=True)
|
||||
def test_flavors_not_detailed(self, fetch_mock, list_mock):
|
||||
res = self.proxy.flavors(details=False)
|
||||
for r in res:
|
||||
self.assertIsNotNone(r)
|
||||
fetch_mock.assert_not_called()
|
||||
list_mock.assert_called_with(
|
||||
flavor.Flavor,
|
||||
base_path="/flavors"
|
||||
)
|
||||
|
||||
@mock.patch("openstack.proxy.Proxy._list", auto_spec=True)
|
||||
@mock.patch("openstack.compute.v2.flavor.Flavor.fetch_extra_specs",
|
||||
auto_spec=True)
|
||||
def test_flavors_query(self, fetch_mock, list_mock):
|
||||
res = self.proxy.flavors(details=False, get_extra_specs=True, a="b")
|
||||
for r in res:
|
||||
fetch_mock.assert_called_with(self.proxy)
|
||||
list_mock.assert_called_with(
|
||||
flavor.Flavor,
|
||||
base_path="/flavors",
|
||||
a="b"
|
||||
)
|
||||
|
||||
@mock.patch("openstack.proxy.Proxy._list", auto_spec=True)
|
||||
@mock.patch("openstack.compute.v2.flavor.Flavor.fetch_extra_specs",
|
||||
auto_spec=True)
|
||||
def test_flavors_get_extra(self, fetch_mock, list_mock):
|
||||
res = self.proxy.flavors(details=False, get_extra_specs=True)
|
||||
for r in res:
|
||||
fetch_mock.assert_called_with(self.proxy)
|
||||
list_mock.assert_called_with(
|
||||
flavor.Flavor,
|
||||
base_path="/flavors"
|
||||
)
|
||||
|
||||
def test_flavor_get_access(self):
|
||||
self._verify("openstack.compute.v2.flavor.Flavor.get_access",
|
||||
|
@ -0,0 +1,4 @@
|
||||
---
|
||||
other:
|
||||
- Flavor operations of the cloud layer are switched to the rely on
|
||||
the proxy layer
|
Loading…
Reference in New Issue
Block a user