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``.
|
:returns: A list of flavor ``munch.Munch``.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
data = proxy._json_response(
|
data = self.compute.flavors(details=True)
|
||||||
self.compute.get(
|
flavors = []
|
||||||
'/flavors/detail', params=dict(is_public='None')),
|
|
||||||
error_message="Error fetching flavor list")
|
|
||||||
flavors = self._normalize_flavors(
|
|
||||||
self._get_and_munchify('flavors', data))
|
|
||||||
|
|
||||||
for flavor in flavors:
|
for flavor in data:
|
||||||
if not flavor.extra_specs and get_extra:
|
if not flavor.extra_specs and get_extra:
|
||||||
endpoint = "/flavors/{id}/os-extra_specs".format(
|
flavor.fetch_extra_specs(self.compute)
|
||||||
id=flavor.id)
|
flavors.append(flavor._to_munch(original_names=False))
|
||||||
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 flavors
|
return flavors
|
||||||
|
|
||||||
def list_server_security_groups(self, server):
|
def list_server_security_groups(self, server):
|
||||||
@ -441,9 +425,12 @@ class ComputeCloudMixin(_normalize.Normalizer):
|
|||||||
found.
|
found.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
search_func = functools.partial(
|
if not filters:
|
||||||
self.search_flavors, get_extra=get_extra)
|
filters = {}
|
||||||
return _utils._get_entity(self, search_func, name_or_id, 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):
|
def get_flavor_by_id(self, id, get_extra=False):
|
||||||
""" Get a flavor by ID
|
""" Get a flavor by ID
|
||||||
@ -454,29 +441,8 @@ class ComputeCloudMixin(_normalize.Normalizer):
|
|||||||
specs.
|
specs.
|
||||||
:returns: A flavor ``munch.Munch``.
|
:returns: A flavor ``munch.Munch``.
|
||||||
"""
|
"""
|
||||||
data = proxy._json_response(
|
flavor = self.compute.get_flavor(id, get_extra_specs=get_extra)
|
||||||
self.compute.get('/flavors/{id}'.format(id=id)),
|
return flavor._to_munch(original_names=False)
|
||||||
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
|
|
||||||
|
|
||||||
def get_server_console(self, server, length=None):
|
def get_server_console(self, server, length=None):
|
||||||
"""Get the console log for a server.
|
"""Get the console log for a server.
|
||||||
@ -1412,27 +1378,23 @@ class ComputeCloudMixin(_normalize.Normalizer):
|
|||||||
|
|
||||||
:raises: OpenStackCloudException on operation error.
|
:raises: OpenStackCloudException on operation error.
|
||||||
"""
|
"""
|
||||||
with _utils.shade_exceptions("Failed to create flavor {name}".format(
|
attrs = {
|
||||||
name=name)):
|
'disk': disk,
|
||||||
payload = {
|
'ephemeral': ephemeral,
|
||||||
'disk': disk,
|
'id': flavorid,
|
||||||
'OS-FLV-EXT-DATA:ephemeral': ephemeral,
|
'is_public': is_public,
|
||||||
'id': flavorid,
|
'name': name,
|
||||||
'os-flavor-access:is_public': is_public,
|
'ram': ram,
|
||||||
'name': name,
|
'rxtx_factor': rxtx_factor,
|
||||||
'ram': ram,
|
'swap': swap,
|
||||||
'rxtx_factor': rxtx_factor,
|
'vcpus': vcpus,
|
||||||
'swap': swap,
|
}
|
||||||
'vcpus': vcpus,
|
if flavorid == 'auto':
|
||||||
}
|
attrs['id'] = None
|
||||||
if flavorid == 'auto':
|
|
||||||
payload['id'] = None
|
|
||||||
data = proxy._json_response(self.compute.post(
|
|
||||||
'/flavors',
|
|
||||||
json=dict(flavor=payload)))
|
|
||||||
|
|
||||||
return self._normalize_flavor(
|
flavor = self.compute.create_flavor(**attrs)
|
||||||
self._get_and_munchify('flavor', data))
|
|
||||||
|
return flavor._to_munch(original_names=False)
|
||||||
|
|
||||||
def delete_flavor(self, name_or_id):
|
def delete_flavor(self, name_or_id):
|
||||||
"""Delete a flavor
|
"""Delete a flavor
|
||||||
@ -1443,19 +1405,17 @@ class ComputeCloudMixin(_normalize.Normalizer):
|
|||||||
|
|
||||||
:raises: OpenStackCloudException on operation error.
|
:raises: OpenStackCloudException on operation error.
|
||||||
"""
|
"""
|
||||||
flavor = self.get_flavor(name_or_id, get_extra=False)
|
try:
|
||||||
if flavor is None:
|
flavor = self.compute.find_flavor(name_or_id)
|
||||||
self.log.debug(
|
if not flavor:
|
||||||
"Flavor %s not found for deleting", name_or_id)
|
self.log.debug(
|
||||||
return False
|
"Flavor %s not found for deleting", name_or_id)
|
||||||
|
return False
|
||||||
proxy._json_response(
|
self.compute.delete_flavor(flavor)
|
||||||
self.compute.delete(
|
return True
|
||||||
'/flavors/{id}'.format(id=flavor['id'])),
|
except exceptions.SDKException:
|
||||||
error_message="Unable to delete flavor {name}".format(
|
raise exceptions.OpenStackCloudException(
|
||||||
name=name_or_id))
|
"Unable to delete flavor {name}".format(name=name_or_id))
|
||||||
|
|
||||||
return True
|
|
||||||
|
|
||||||
def set_flavor_specs(self, flavor_id, extra_specs):
|
def set_flavor_specs(self, flavor_id, extra_specs):
|
||||||
"""Add extra specs to a flavor
|
"""Add extra specs to a flavor
|
||||||
@ -1466,11 +1426,7 @@ class ComputeCloudMixin(_normalize.Normalizer):
|
|||||||
:raises: OpenStackCloudException on operation error.
|
:raises: OpenStackCloudException on operation error.
|
||||||
:raises: OpenStackCloudResourceNotFound if flavor ID is not found.
|
:raises: OpenStackCloudResourceNotFound if flavor ID is not found.
|
||||||
"""
|
"""
|
||||||
proxy._json_response(
|
self.compute.create_flavor_extra_specs(flavor_id, extra_specs)
|
||||||
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")
|
|
||||||
|
|
||||||
def unset_flavor_specs(self, flavor_id, keys):
|
def unset_flavor_specs(self, flavor_id, keys):
|
||||||
"""Delete extra specs from a flavor
|
"""Delete extra specs from a flavor
|
||||||
@ -1482,24 +1438,7 @@ class ComputeCloudMixin(_normalize.Normalizer):
|
|||||||
:raises: OpenStackCloudResourceNotFound if flavor ID is not found.
|
:raises: OpenStackCloudResourceNotFound if flavor ID is not found.
|
||||||
"""
|
"""
|
||||||
for key in keys:
|
for key in keys:
|
||||||
proxy._json_response(
|
self.compute.delete_flavor_extra_specs_property(flavor_id, key)
|
||||||
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}))
|
|
||||||
|
|
||||||
def add_flavor_access(self, flavor_id, project_id):
|
def add_flavor_access(self, flavor_id, project_id):
|
||||||
"""Grant access to a private flavor for a project/tenant.
|
"""Grant access to a private flavor for a project/tenant.
|
||||||
@ -1509,7 +1448,7 @@ class ComputeCloudMixin(_normalize.Normalizer):
|
|||||||
|
|
||||||
:raises: OpenStackCloudException on operation error.
|
: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):
|
def remove_flavor_access(self, flavor_id, project_id):
|
||||||
"""Revoke access from a private flavor for a project/tenant.
|
"""Revoke access from a private flavor for a project/tenant.
|
||||||
@ -1519,7 +1458,7 @@ class ComputeCloudMixin(_normalize.Normalizer):
|
|||||||
|
|
||||||
:raises: OpenStackCloudException on operation error.
|
: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):
|
def list_flavor_access(self, flavor_id):
|
||||||
"""List access from a private flavor for a project/tenant.
|
"""List access from a private flavor for a project/tenant.
|
||||||
@ -1530,14 +1469,8 @@ class ComputeCloudMixin(_normalize.Normalizer):
|
|||||||
|
|
||||||
:raises: OpenStackCloudException on operation error.
|
:raises: OpenStackCloudException on operation error.
|
||||||
"""
|
"""
|
||||||
data = proxy._json_response(
|
access = self.compute.get_flavor_access(flavor_id)
|
||||||
self.compute.get(
|
return _utils.normalize_flavor_accesses(access)
|
||||||
'/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))
|
|
||||||
|
|
||||||
def list_hypervisors(self, filters={}):
|
def list_hypervisors(self, filters={}):
|
||||||
"""List all hypervisors
|
"""List all hypervisors
|
||||||
|
@ -62,7 +62,7 @@ class Proxy(proxy.Proxy):
|
|||||||
# ========== Flavors ==========
|
# ========== Flavors ==========
|
||||||
|
|
||||||
def find_flavor(self, name_or_id, ignore_missing=True,
|
def find_flavor(self, name_or_id, ignore_missing=True,
|
||||||
get_extra_specs=False):
|
get_extra_specs=False, **query):
|
||||||
"""Find a single flavor
|
"""Find a single flavor
|
||||||
|
|
||||||
:param name_or_id: The name or ID of a 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
|
:param bool get_extra_specs: When set to ``True`` and extra_specs not
|
||||||
present in the response will invoke additional API call to fetch
|
present in the response will invoke additional API call to fetch
|
||||||
extra_specs.
|
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
|
:returns: One :class:`~openstack.compute.v2.flavor.Flavor` or None
|
||||||
"""
|
"""
|
||||||
flavor = self._find(_flavor.Flavor, name_or_id,
|
flavor = self._find(
|
||||||
ignore_missing=ignore_missing)
|
_flavor.Flavor, name_or_id, ignore_missing=ignore_missing, **query)
|
||||||
if flavor and get_extra_specs and not flavor.extra_specs:
|
if flavor and get_extra_specs and not flavor.extra_specs:
|
||||||
flavor = flavor.fetch_extra_specs(self)
|
flavor = flavor.fetch_extra_specs(self)
|
||||||
return flavor
|
return flavor
|
||||||
@ -126,19 +130,25 @@ class Proxy(proxy.Proxy):
|
|||||||
flavor = flavor.fetch_extra_specs(self)
|
flavor = flavor.fetch_extra_specs(self)
|
||||||
return flavor
|
return flavor
|
||||||
|
|
||||||
def flavors(self, details=True, **query):
|
def flavors(self, details=True, get_extra_specs=False, **query):
|
||||||
"""Return a generator of flavors
|
"""Return a generator of flavors
|
||||||
|
|
||||||
:param bool details: When ``True``, returns
|
:param bool details: When ``True``, returns
|
||||||
:class:`~openstack.compute.v2.flavor.Flavor` objects,
|
:class:`~openstack.compute.v2.flavor.Flavor` objects,
|
||||||
with additional attributes filled.
|
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
|
:param kwargs query: Optional query parameters to be sent to limit
|
||||||
the flavors being returned.
|
the flavors being returned.
|
||||||
|
|
||||||
:returns: A generator of flavor objects
|
:returns: A generator of flavor objects
|
||||||
"""
|
"""
|
||||||
base_path = '/flavors/detail' if details else '/flavors'
|
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):
|
def flavor_add_tenant_access(self, flavor, tenant):
|
||||||
"""Adds tenant/project access to flavor.
|
"""Adds tenant/project access to flavor.
|
||||||
|
@ -62,7 +62,7 @@ class Flavor(resource.Resource):
|
|||||||
# TODO(mordred) extra_specs can historically also come from
|
# TODO(mordred) extra_specs can historically also come from
|
||||||
# OS-FLV-WITH-EXT-SPECS:extra_specs. Do we care?
|
# OS-FLV-WITH-EXT-SPECS:extra_specs. Do we care?
|
||||||
#: A dictionary of the flavor's extra-specs key-and-value pairs.
|
#: 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
|
@classmethod
|
||||||
def list(cls, session, paginated=True, base_path='/flavors/detail',
|
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
|
# We should also always have ephemeral and public attributes
|
||||||
self.assertIn('ephemeral', flavor)
|
self.assertIn('ephemeral', flavor)
|
||||||
self.assertIn('OS-FLV-EXT-DATA:ephemeral', flavor)
|
|
||||||
self.assertEqual(5, flavor['ephemeral'])
|
self.assertEqual(5, flavor['ephemeral'])
|
||||||
self.assertIn('is_public', flavor)
|
self.assertIn('is_public', flavor)
|
||||||
self.assertIn('os-flavor-access:is_public', flavor)
|
|
||||||
self.assertTrue(flavor['is_public'])
|
self.assertTrue(flavor['is_public'])
|
||||||
|
|
||||||
for key in flavor_kwargs.keys():
|
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
|
||||||
import openstack.cloud
|
import openstack.cloud
|
||||||
from openstack.cloud import meta
|
from openstack.cloud import meta
|
||||||
|
from openstack.compute.v2 import flavor as _flavor
|
||||||
from openstack import exceptions
|
from openstack import exceptions
|
||||||
from openstack.tests import fakes
|
from openstack.tests import fakes
|
||||||
from openstack.tests.unit import base
|
from openstack.tests.unit import base
|
||||||
@ -436,10 +437,16 @@ class TestMemoryCache(base.TestCase):
|
|||||||
endpoint=fakes.COMPUTE_ENDPOINT)
|
endpoint=fakes.COMPUTE_ENDPOINT)
|
||||||
|
|
||||||
uris_to_mock = [
|
uris_to_mock = [
|
||||||
dict(method='GET', uri=mock_uri, json={'flavors': []}),
|
|
||||||
dict(method='GET', uri=mock_uri,
|
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})
|
json={'flavors': fakes.FAKE_FLAVOR_LIST})
|
||||||
]
|
]
|
||||||
|
self.use_compute_discovery()
|
||||||
|
|
||||||
self.register_uris(uris_to_mock)
|
self.register_uris(uris_to_mock)
|
||||||
|
|
||||||
@ -447,8 +454,11 @@ class TestMemoryCache(base.TestCase):
|
|||||||
|
|
||||||
self.assertEqual([], self.cloud.list_flavors())
|
self.assertEqual([], self.cloud.list_flavors())
|
||||||
|
|
||||||
fake_flavor_dicts = self.cloud._normalize_flavors(
|
fake_flavor_dicts = [
|
||||||
fakes.FAKE_FLAVOR_LIST)
|
_flavor.Flavor(connection=self.cloud, **f)
|
||||||
|
for f in fakes.FAKE_FLAVOR_LIST
|
||||||
|
]
|
||||||
|
|
||||||
self.cloud.list_flavors.invalidate(self.cloud)
|
self.cloud.list_flavors.invalidate(self.cloud)
|
||||||
self.assertEqual(fake_flavor_dicts, self.cloud.list_flavors())
|
self.assertEqual(fake_flavor_dicts, self.cloud.list_flavors())
|
||||||
|
|
||||||
|
@ -791,11 +791,12 @@ class TestCreateServer(base.TestCase):
|
|||||||
dict(method='GET',
|
dict(method='GET',
|
||||||
uri='https://image.example.com/v2/images',
|
uri='https://image.example.com/v2/images',
|
||||||
json=fake_image_search_return),
|
json=fake_image_search_return),
|
||||||
|
self.get_nova_discovery_mock_dict(),
|
||||||
dict(method='GET',
|
dict(method='GET',
|
||||||
uri=self.get_mock_url(
|
uri=self.get_mock_url(
|
||||||
'compute', 'public', append=['flavors', 'detail'],
|
'compute', 'public', append=['flavors', 'vanilla'],
|
||||||
qs_elements=['is_public=None']),
|
qs_elements=[]),
|
||||||
json={'flavors': fakes.FAKE_FLAVOR_LIST}),
|
json=fakes.FAKE_FLAVOR),
|
||||||
dict(method='POST',
|
dict(method='POST',
|
||||||
uri=self.get_mock_url(
|
uri=self.get_mock_url(
|
||||||
'compute', 'public', append=['servers']),
|
'compute', 'public', append=['servers']),
|
||||||
|
@ -18,8 +18,13 @@ from openstack.tests.unit import base
|
|||||||
|
|
||||||
class TestFlavors(base.TestCase):
|
class TestFlavors(base.TestCase):
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
super(TestFlavors, self).setUp()
|
||||||
|
# self.use_compute_discovery()
|
||||||
|
|
||||||
def test_create_flavor(self):
|
def test_create_flavor(self):
|
||||||
|
|
||||||
|
self.use_compute_discovery()
|
||||||
self.register_uris([
|
self.register_uris([
|
||||||
dict(method='POST',
|
dict(method='POST',
|
||||||
uri='{endpoint}/flavors'.format(
|
uri='{endpoint}/flavors'.format(
|
||||||
@ -44,11 +49,12 @@ class TestFlavors(base.TestCase):
|
|||||||
self.assert_calls()
|
self.assert_calls()
|
||||||
|
|
||||||
def test_delete_flavor(self):
|
def test_delete_flavor(self):
|
||||||
|
self.use_compute_discovery()
|
||||||
self.register_uris([
|
self.register_uris([
|
||||||
dict(method='GET',
|
dict(method='GET',
|
||||||
uri='{endpoint}/flavors/detail?is_public=None'.format(
|
uri='{endpoint}/flavors/vanilla'.format(
|
||||||
endpoint=fakes.COMPUTE_ENDPOINT),
|
endpoint=fakes.COMPUTE_ENDPOINT),
|
||||||
json={'flavors': fakes.FAKE_FLAVOR_LIST}),
|
json=fakes.FAKE_FLAVOR),
|
||||||
dict(method='DELETE',
|
dict(method='DELETE',
|
||||||
uri='{endpoint}/flavors/{id}'.format(
|
uri='{endpoint}/flavors/{id}'.format(
|
||||||
endpoint=fakes.COMPUTE_ENDPOINT, id=fakes.FLAVOR_ID))])
|
endpoint=fakes.COMPUTE_ENDPOINT, id=fakes.FLAVOR_ID))])
|
||||||
@ -57,7 +63,12 @@ class TestFlavors(base.TestCase):
|
|||||||
self.assert_calls()
|
self.assert_calls()
|
||||||
|
|
||||||
def test_delete_flavor_not_found(self):
|
def test_delete_flavor_not_found(self):
|
||||||
|
self.use_compute_discovery()
|
||||||
self.register_uris([
|
self.register_uris([
|
||||||
|
dict(method='GET',
|
||||||
|
uri='{endpoint}/flavors/invalid'.format(
|
||||||
|
endpoint=fakes.COMPUTE_ENDPOINT),
|
||||||
|
status_code=404),
|
||||||
dict(method='GET',
|
dict(method='GET',
|
||||||
uri='{endpoint}/flavors/detail?is_public=None'.format(
|
uri='{endpoint}/flavors/detail?is_public=None'.format(
|
||||||
endpoint=fakes.COMPUTE_ENDPOINT),
|
endpoint=fakes.COMPUTE_ENDPOINT),
|
||||||
@ -68,7 +79,12 @@ class TestFlavors(base.TestCase):
|
|||||||
self.assert_calls()
|
self.assert_calls()
|
||||||
|
|
||||||
def test_delete_flavor_exception(self):
|
def test_delete_flavor_exception(self):
|
||||||
|
self.use_compute_discovery()
|
||||||
self.register_uris([
|
self.register_uris([
|
||||||
|
dict(method='GET',
|
||||||
|
uri='{endpoint}/flavors/vanilla'.format(
|
||||||
|
endpoint=fakes.COMPUTE_ENDPOINT),
|
||||||
|
json=fakes.FAKE_FLAVOR),
|
||||||
dict(method='GET',
|
dict(method='GET',
|
||||||
uri='{endpoint}/flavors/detail?is_public=None'.format(
|
uri='{endpoint}/flavors/detail?is_public=None'.format(
|
||||||
endpoint=fakes.COMPUTE_ENDPOINT),
|
endpoint=fakes.COMPUTE_ENDPOINT),
|
||||||
@ -82,6 +98,7 @@ class TestFlavors(base.TestCase):
|
|||||||
self.cloud.delete_flavor, 'vanilla')
|
self.cloud.delete_flavor, 'vanilla')
|
||||||
|
|
||||||
def test_list_flavors(self):
|
def test_list_flavors(self):
|
||||||
|
self.use_compute_discovery()
|
||||||
uris_to_mock = [
|
uris_to_mock = [
|
||||||
dict(method='GET',
|
dict(method='GET',
|
||||||
uri='{endpoint}/flavors/detail?is_public=None'.format(
|
uri='{endpoint}/flavors/detail?is_public=None'.format(
|
||||||
@ -106,6 +123,7 @@ class TestFlavors(base.TestCase):
|
|||||||
self.assert_calls()
|
self.assert_calls()
|
||||||
|
|
||||||
def test_list_flavors_with_extra(self):
|
def test_list_flavors_with_extra(self):
|
||||||
|
self.use_compute_discovery()
|
||||||
uris_to_mock = [
|
uris_to_mock = [
|
||||||
dict(method='GET',
|
dict(method='GET',
|
||||||
uri='{endpoint}/flavors/detail?is_public=None'.format(
|
uri='{endpoint}/flavors/detail?is_public=None'.format(
|
||||||
@ -136,6 +154,7 @@ class TestFlavors(base.TestCase):
|
|||||||
self.assert_calls()
|
self.assert_calls()
|
||||||
|
|
||||||
def test_get_flavor_by_ram(self):
|
def test_get_flavor_by_ram(self):
|
||||||
|
self.use_compute_discovery()
|
||||||
uris_to_mock = [
|
uris_to_mock = [
|
||||||
dict(method='GET',
|
dict(method='GET',
|
||||||
uri='{endpoint}/flavors/detail?is_public=None'.format(
|
uri='{endpoint}/flavors/detail?is_public=None'.format(
|
||||||
@ -154,6 +173,7 @@ class TestFlavors(base.TestCase):
|
|||||||
self.assertEqual(fakes.STRAWBERRY_FLAVOR_ID, flavor['id'])
|
self.assertEqual(fakes.STRAWBERRY_FLAVOR_ID, flavor['id'])
|
||||||
|
|
||||||
def test_get_flavor_by_ram_and_include(self):
|
def test_get_flavor_by_ram_and_include(self):
|
||||||
|
self.use_compute_discovery()
|
||||||
uris_to_mock = [
|
uris_to_mock = [
|
||||||
dict(method='GET',
|
dict(method='GET',
|
||||||
uri='{endpoint}/flavors/detail?is_public=None'.format(
|
uri='{endpoint}/flavors/detail?is_public=None'.format(
|
||||||
@ -171,6 +191,7 @@ class TestFlavors(base.TestCase):
|
|||||||
self.assertEqual(fakes.STRAWBERRY_FLAVOR_ID, flavor['id'])
|
self.assertEqual(fakes.STRAWBERRY_FLAVOR_ID, flavor['id'])
|
||||||
|
|
||||||
def test_get_flavor_by_ram_not_found(self):
|
def test_get_flavor_by_ram_not_found(self):
|
||||||
|
self.use_compute_discovery()
|
||||||
self.register_uris([
|
self.register_uris([
|
||||||
dict(method='GET',
|
dict(method='GET',
|
||||||
uri='{endpoint}/flavors/detail?is_public=None'.format(
|
uri='{endpoint}/flavors/detail?is_public=None'.format(
|
||||||
@ -182,19 +203,19 @@ class TestFlavors(base.TestCase):
|
|||||||
ram=100)
|
ram=100)
|
||||||
|
|
||||||
def test_get_flavor_string_and_int(self):
|
def test_get_flavor_string_and_int(self):
|
||||||
flavor_list_uri = '{endpoint}/flavors/detail?is_public=None'.format(
|
self.use_compute_discovery()
|
||||||
endpoint=fakes.COMPUTE_ENDPOINT)
|
|
||||||
flavor_resource_uri = '{endpoint}/flavors/1/os-extra_specs'.format(
|
flavor_resource_uri = '{endpoint}/flavors/1/os-extra_specs'.format(
|
||||||
endpoint=fakes.COMPUTE_ENDPOINT)
|
endpoint=fakes.COMPUTE_ENDPOINT)
|
||||||
flavor_list_json = {'flavors': [fakes.make_fake_flavor(
|
flavor = fakes.make_fake_flavor('1', 'vanilla')
|
||||||
'1', 'vanilla')]}
|
|
||||||
flavor_json = {'extra_specs': {}}
|
flavor_json = {'extra_specs': {}}
|
||||||
|
|
||||||
self.register_uris([
|
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_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')
|
flavor1 = self.cloud.get_flavor('1')
|
||||||
self.assertEqual('1', flavor1['id'])
|
self.assertEqual('1', flavor1['id'])
|
||||||
@ -202,6 +223,7 @@ class TestFlavors(base.TestCase):
|
|||||||
self.assertEqual('1', flavor2['id'])
|
self.assertEqual('1', flavor2['id'])
|
||||||
|
|
||||||
def test_set_flavor_specs(self):
|
def test_set_flavor_specs(self):
|
||||||
|
self.use_compute_discovery()
|
||||||
extra_specs = dict(key1='value1')
|
extra_specs = dict(key1='value1')
|
||||||
self.register_uris([
|
self.register_uris([
|
||||||
dict(method='POST',
|
dict(method='POST',
|
||||||
@ -213,6 +235,7 @@ class TestFlavors(base.TestCase):
|
|||||||
self.assert_calls()
|
self.assert_calls()
|
||||||
|
|
||||||
def test_unset_flavor_specs(self):
|
def test_unset_flavor_specs(self):
|
||||||
|
self.use_compute_discovery()
|
||||||
keys = ['key1', 'key2']
|
keys = ['key1', 'key2']
|
||||||
self.register_uris([
|
self.register_uris([
|
||||||
dict(method='DELETE',
|
dict(method='DELETE',
|
||||||
@ -262,6 +285,7 @@ class TestFlavors(base.TestCase):
|
|||||||
self.assert_calls()
|
self.assert_calls()
|
||||||
|
|
||||||
def test_get_flavor_by_id(self):
|
def test_get_flavor_by_id(self):
|
||||||
|
self.use_compute_discovery()
|
||||||
flavor_uri = '{endpoint}/flavors/1'.format(
|
flavor_uri = '{endpoint}/flavors/1'.format(
|
||||||
endpoint=fakes.COMPUTE_ENDPOINT)
|
endpoint=fakes.COMPUTE_ENDPOINT)
|
||||||
flavor_json = {'flavor': fakes.make_fake_flavor('1', 'vanilla')}
|
flavor_json = {'flavor': fakes.make_fake_flavor('1', 'vanilla')}
|
||||||
@ -278,6 +302,7 @@ class TestFlavors(base.TestCase):
|
|||||||
self.assertEqual({}, flavor2.extra_specs)
|
self.assertEqual({}, flavor2.extra_specs)
|
||||||
|
|
||||||
def test_get_flavor_with_extra_specs(self):
|
def test_get_flavor_with_extra_specs(self):
|
||||||
|
self.use_compute_discovery()
|
||||||
flavor_uri = '{endpoint}/flavors/1'.format(
|
flavor_uri = '{endpoint}/flavors/1'.format(
|
||||||
endpoint=fakes.COMPUTE_ENDPOINT)
|
endpoint=fakes.COMPUTE_ENDPOINT)
|
||||||
flavor_extra_uri = '{endpoint}/flavors/1/os-extra_specs'.format(
|
flavor_extra_uri = '{endpoint}/flavors/1/os-extra_specs'.format(
|
||||||
|
@ -47,6 +47,13 @@ class TestFlavor(TestComputeProxy):
|
|||||||
def test_flavor_find(self):
|
def test_flavor_find(self):
|
||||||
self.verify_find(self.proxy.find_flavor, flavor.Flavor)
|
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):
|
def test_flavor_find_fetch_extra(self):
|
||||||
"""fetch extra_specs is triggered"""
|
"""fetch extra_specs is triggered"""
|
||||||
with mock.patch(
|
with mock.patch(
|
||||||
@ -129,17 +136,56 @@ class TestFlavor(TestComputeProxy):
|
|||||||
)
|
)
|
||||||
mocked.assert_not_called()
|
mocked.assert_not_called()
|
||||||
|
|
||||||
def test_flavors_detailed(self):
|
@mock.patch("openstack.proxy.Proxy._list", auto_spec=True)
|
||||||
self.verify_list(self.proxy.flavors, flavor.FlavorDetail,
|
@mock.patch("openstack.compute.v2.flavor.Flavor.fetch_extra_specs",
|
||||||
method_kwargs={"details": True, "query": 1},
|
auto_spec=True)
|
||||||
expected_kwargs={"query": 1,
|
def test_flavors_detailed(self, fetch_mock, list_mock):
|
||||||
"base_path": "/flavors/detail"})
|
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):
|
@mock.patch("openstack.proxy.Proxy._list", auto_spec=True)
|
||||||
self.verify_list(self.proxy.flavors, flavor.Flavor,
|
@mock.patch("openstack.compute.v2.flavor.Flavor.fetch_extra_specs",
|
||||||
method_kwargs={"details": False, "query": 1},
|
auto_spec=True)
|
||||||
expected_kwargs={"query": 1,
|
def test_flavors_not_detailed(self, fetch_mock, list_mock):
|
||||||
"base_path": "/flavors"})
|
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):
|
def test_flavor_get_access(self):
|
||||||
self._verify("openstack.compute.v2.flavor.Flavor.get_access",
|
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