Files
nova/nova/tests/unit/api/openstack/compute/test_flavors.py
T
Stephen Finucane b95a2c5219 api: Remove dead fields from flavors response
Change-Id: I65be4f2e522c9f73a28b8837d7937a371d3e73d3
Signed-off-by: Stephen Finucane <stephenfin@redhat.com>
2026-02-26 21:39:10 +00:00

1055 lines
40 KiB
Python

# Copyright 2012 OpenStack Foundation
# All Rights Reserved.
#
# 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.
from unittest import mock
from urllib import parse as urlparse
import webob
from nova.api.openstack import common
from nova.api.openstack.compute import flavors
from nova import context
from nova import exception
from nova import objects
from nova import test
from nova.tests.unit.api.openstack import fakes
from nova.tests.unit import matchers
def fake_get_limit_and_marker(request, max_limit=1):
params = common.get_pagination_params(request)
limit = params.get('limit', max_limit)
limit = min(max_limit, limit)
marker = params.get('marker')
return limit, marker
def return_flavor_not_found(context, flavor_id, read_deleted=None):
raise exception.FlavorNotFound(flavor_id=flavor_id)
class FlavorsTestV21(test.TestCase):
microversion = '2.1'
# Flag to tell the test if a description should be expected in a response.
expect_description = False
# Flag to tell the test if a extra_specs should be expected in a response.
expect_extra_specs = False
# Flag to tell the test if legacy fields should be omitted from responses.
omit_legacy_fields = False
def setUp(self):
super().setUp()
fakes.stub_out_networking(self)
fakes.stub_out_flavor_get_all(self)
fakes.stub_out_flavor_get_by_flavor_id(self)
self.controller = flavors.FlavorsController()
def _build_request(self, url):
return fakes.HTTPRequestV21.blank(
url, version=self.microversion, base_url='http://localhost/v2.1')
def _set_expected_body(self, expected, flavor):
expected['OS-FLV-EXT-DATA:ephemeral'] = flavor.ephemeral_gb
expected['OS-FLV-DISABLED:disabled'] = flavor.disabled
expected['swap'] = flavor.swap
if self.expect_description:
expected['description'] = flavor.description
if self.expect_extra_specs:
expected['extra_specs'] = flavor.extra_specs
if self.omit_legacy_fields:
expected.pop('rxtx_factor', None)
expected.pop('OS-FLV-DISABLED:disabled', None)
@mock.patch('nova.objects.Flavor.get_by_flavor_id',
side_effect=return_flavor_not_found)
def test_get_flavor_by_invalid_id(self, mock_get):
req = self._build_request('/flavors/asdf')
self.assertRaises(webob.exc.HTTPNotFound,
self.controller.show, req, 'asdf')
def test_get_flavor_by_id(self):
req = self._build_request('/flavors/1')
flavor = self.controller.show(req, '1')
expected = {
"flavor": {
"id": fakes.FLAVORS['1'].flavorid,
"name": fakes.FLAVORS['1'].name,
"ram": fakes.FLAVORS['1'].memory_mb,
"disk": fakes.FLAVORS['1'].root_gb,
"vcpus": fakes.FLAVORS['1'].vcpus,
"os-flavor-access:is_public": True,
"rxtx_factor": 1.0,
"links": [
{
"rel": "self",
"href": "http://localhost/v2.1/flavors/1",
},
{
"rel": "bookmark",
"href": "http://localhost/flavors/1",
},
],
},
}
self._set_expected_body(expected['flavor'], fakes.FLAVORS['1'])
self.assertEqual(flavor, expected)
def test_get_flavor_with_custom_link_prefix(self):
self.flags(compute_link_prefix='http://zoo.com:42',
glance_link_prefix='http://circus.com:34',
group='api')
req = self._build_request('/flavors/1')
flavor = self.controller.show(req, '1')
expected = {
"flavor": {
"id": fakes.FLAVORS['1'].flavorid,
"name": fakes.FLAVORS['1'].name,
"ram": fakes.FLAVORS['1'].memory_mb,
"disk": fakes.FLAVORS['1'].root_gb,
"vcpus": fakes.FLAVORS['1'].vcpus,
"os-flavor-access:is_public": True,
"rxtx_factor": 1.0,
"links": [
{
"rel": "self",
"href": "http://zoo.com:42/v2.1/flavors/1",
},
{
"rel": "bookmark",
"href": "http://zoo.com:42/flavors/1",
},
],
},
}
self._set_expected_body(expected['flavor'], fakes.FLAVORS['1'])
self.assertEqual(expected, flavor)
def test_get_flavor_list(self):
req = self._build_request('/flavors')
flavor = self.controller.index(req)
expected = {
"flavors": [
{
"id": fakes.FLAVORS['1'].flavorid,
"name": fakes.FLAVORS['1'].name,
"links": [
{
"rel": "self",
"href": "http://localhost/v2.1/flavors/1",
},
{
"rel": "bookmark",
"href": "http://localhost/flavors/1",
},
],
},
{
"id": fakes.FLAVORS['2'].flavorid,
"name": fakes.FLAVORS['2'].name,
"links": [
{
"rel": "self",
"href": "http://localhost/v2.1/flavors/2",
},
{
"rel": "bookmark",
"href": "http://localhost/flavors/2",
},
],
},
],
}
if self.expect_description:
for idx, _flavor in enumerate(expected['flavors']):
expected['flavors'][idx]['description'] = (
fakes.FLAVORS[_flavor['id']].description)
self.assertEqual(flavor, expected)
def test_get_flavor_list_with_marker(self):
self.maxDiff = None
url = '/flavors?limit=1&marker=1'
req = self._build_request(url)
flavor = self.controller.index(req)
expected = {
"flavors": [
{
"id": fakes.FLAVORS['2'].flavorid,
"name": fakes.FLAVORS['2'].name,
"links": [
{
"rel": "self",
"href": "http://localhost/v2.1/flavors/2",
},
{
"rel": "bookmark",
"href": "http://localhost/flavors/2",
},
],
},
],
'flavors_links': [
{'href': 'http://localhost/v2.1/flavors?limit=1&marker=2',
'rel': 'next'}
]
}
if self.expect_description:
expected['flavors'][0]['description'] = (
fakes.FLAVORS['2'].description)
self.assertThat(flavor, matchers.DictMatches(expected))
def test_get_flavor_list_with_invalid_marker(self):
req = self._build_request('/flavors?marker=99999')
self.assertRaises(webob.exc.HTTPBadRequest,
self.controller.index, req)
def test_get_flavor_detail_with_limit(self):
url = '/flavors/detail?limit=1'
req = self._build_request(url)
response = self.controller.detail(req)
response_list = response["flavors"]
response_links = response["flavors_links"]
expected_flavors = [
{
"id": fakes.FLAVORS['1'].flavorid,
"name": fakes.FLAVORS['1'].name,
"ram": fakes.FLAVORS['1'].memory_mb,
"disk": fakes.FLAVORS['1'].root_gb,
"vcpus": fakes.FLAVORS['1'].vcpus,
"os-flavor-access:is_public": True,
"rxtx_factor": 1.0,
"links": [
{
"rel": "self",
"href": "http://localhost/v2.1/flavors/1",
},
{
"rel": "bookmark",
"href": "http://localhost/flavors/1",
},
],
},
]
self._set_expected_body(expected_flavors[0], fakes.FLAVORS['1'])
self.assertEqual(response_list, expected_flavors)
self.assertEqual(response_links[0]['rel'], 'next')
href_parts = urlparse.urlparse(response_links[0]['href'])
self.assertEqual('/v2.1/flavors/detail', href_parts.path)
params = urlparse.parse_qs(href_parts.query)
self.assertThat({'limit': ['1'], 'marker': ['1']},
matchers.DictMatches(params))
def test_get_flavor_list_with_limit(self):
req = self._build_request('/flavors?limit=2')
response = self.controller.index(req)
response_list = response["flavors"]
response_links = response["flavors_links"]
expected_flavors = [
{
"id": fakes.FLAVORS['1'].flavorid,
"name": fakes.FLAVORS['1'].name,
"links": [
{
"rel": "self",
"href": "http://localhost/v2.1/flavors/1",
},
{
"rel": "bookmark",
"href": "http://localhost/flavors/1",
},
],
},
{
"id": fakes.FLAVORS['2'].flavorid,
"name": fakes.FLAVORS['2'].name,
"links": [
{
"rel": "self",
"href": "http://localhost/v2.1/flavors/2",
},
{
"rel": "bookmark",
"href": "http://localhost/flavors/2",
},
],
}
]
if self.expect_description:
for idx, _flavor in enumerate(expected_flavors):
expected_flavors[idx]['description'] = (
fakes.FLAVORS[_flavor['id']].description)
self.assertEqual(response_list, expected_flavors)
self.assertEqual(response_links[0]['rel'], 'next')
href_parts = urlparse.urlparse(response_links[0]['href'])
self.assertEqual('/v2.1/flavors', href_parts.path)
params = urlparse.parse_qs(href_parts.query)
self.assertThat({'limit': ['2'], 'marker': ['2']},
matchers.DictMatches(params))
def test_get_flavor_list_with_default_limit(self):
self.stub_out('nova.api.openstack.common.get_limit_and_marker',
fake_get_limit_and_marker)
self.flags(max_limit=1, group='api')
req = self._build_request('/flavors?limit=2')
response = self.controller.index(req)
response_list = response["flavors"]
response_links = response["flavors_links"]
expected_flavors = [
{
"id": fakes.FLAVORS['1'].flavorid,
"name": fakes.FLAVORS['1'].name,
"links": [
{
"rel": "self",
"href": "http://localhost/v2.1/flavors/1",
},
{
"rel": "bookmark",
"href": "http://localhost/flavors/1",
}
]
}
]
if self.expect_description:
expected_flavors[0]['description'] = (
fakes.FLAVORS['1'].description)
self.assertEqual(response_list, expected_flavors)
self.assertEqual(response_links[0]['rel'], 'next')
href_parts = urlparse.urlparse(response_links[0]['href'])
self.assertEqual('/v2.1/flavors', href_parts.path)
params = urlparse.parse_qs(href_parts.query)
self.assertThat({'limit': ['2'], 'marker': ['1']},
matchers.DictMatches(params))
def test_get_flavor_list_detail(self):
req = self._build_request('/flavors/detail')
flavor = self.controller.detail(req)
expected = {
"flavors": [
{
"id": fakes.FLAVORS['1'].flavorid,
"name": fakes.FLAVORS['1'].name,
"ram": fakes.FLAVORS['1'].memory_mb,
"disk": fakes.FLAVORS['1'].root_gb,
"vcpus": fakes.FLAVORS['1'].vcpus,
"os-flavor-access:is_public": True,
"rxtx_factor": 1.0,
"links": [
{
"rel": "self",
"href": "http://localhost/v2.1/flavors/1",
},
{
"rel": "bookmark",
"href": "http://localhost/flavors/1",
},
],
},
{
"id": fakes.FLAVORS['2'].flavorid,
"name": fakes.FLAVORS['2'].name,
"ram": fakes.FLAVORS['2'].memory_mb,
"disk": fakes.FLAVORS['2'].root_gb,
"vcpus": fakes.FLAVORS['2'].vcpus,
"os-flavor-access:is_public": True,
"rxtx_factor": '',
"links": [
{
"rel": "self",
"href": "http://localhost/v2.1/flavors/2",
},
{
"rel": "bookmark",
"href": "http://localhost/flavors/2",
},
],
},
],
}
self._set_expected_body(expected['flavors'][0], fakes.FLAVORS['1'])
self._set_expected_body(expected['flavors'][1], fakes.FLAVORS['2'])
self.assertEqual(expected, flavor)
@mock.patch('nova.objects.FlavorList.get_all',
return_value=objects.FlavorList())
def test_get_empty_flavor_list(self, mock_get):
req = self._build_request('/flavors')
flavors = self.controller.index(req)
expected = {'flavors': []}
self.assertEqual(flavors, expected)
def test_get_flavor_list_filter_min_ram(self):
# Flavor lists may be filtered by minRam.
req = self._build_request('/flavors?minRam=512')
flavor = self.controller.index(req)
expected = {
"flavors": [
{
"id": fakes.FLAVORS['2'].flavorid,
"name": fakes.FLAVORS['2'].name,
"links": [
{
"rel": "self",
"href": "http://localhost/v2.1/flavors/2",
},
{
"rel": "bookmark",
"href": "http://localhost/flavors/2",
},
],
},
],
}
if self.expect_description:
expected['flavors'][0]['description'] = (
fakes.FLAVORS['2'].description)
self.assertEqual(flavor, expected)
def test_get_flavor_list_filter_invalid_min_ram(self):
# Ensure you cannot list flavors with invalid minRam param.
req = self._build_request('/flavors?minRam=NaN')
self.assertRaises(webob.exc.HTTPBadRequest,
self.controller.index, req)
def test_get_flavor_list_filter_min_disk(self):
# Flavor lists may be filtered by minDisk.
req = self._build_request('/flavors?minDisk=20')
flavor = self.controller.index(req)
expected = {
"flavors": [
{
"id": fakes.FLAVORS['2'].flavorid,
"name": fakes.FLAVORS['2'].name,
"links": [
{
"rel": "self",
"href": "http://localhost/v2.1/flavors/2",
},
{
"rel": "bookmark",
"href": "http://localhost/flavors/2",
},
],
},
],
}
if self.expect_description:
expected['flavors'][0]['description'] = (
fakes.FLAVORS['2'].description)
self.assertEqual(flavor, expected)
def test_get_flavor_list_filter_invalid_min_disk(self):
# Ensure you cannot list flavors with invalid minDisk param.
req = self._build_request('/flavors?minDisk=NaN')
self.assertRaises(webob.exc.HTTPBadRequest,
self.controller.index, req)
def test_get_flavor_list_detail_min_ram_and_min_disk(self):
"""Tests that filtering work on flavor details and that minRam and
minDisk filters can be combined
"""
req = self._build_request('/flavors/detail?minRam=256&minDisk=20')
flavor = self.controller.detail(req)
expected = {
"flavors": [
{
"id": fakes.FLAVORS['2'].flavorid,
"name": fakes.FLAVORS['2'].name,
"ram": fakes.FLAVORS['2'].memory_mb,
"disk": fakes.FLAVORS['2'].root_gb,
"vcpus": fakes.FLAVORS['2'].vcpus,
"os-flavor-access:is_public": True,
"rxtx_factor": '',
"links": [
{
"rel": "self",
"href": "http://localhost/v2.1/flavors/2",
},
{
"rel": "bookmark",
"href": "http://localhost/flavors/2",
},
],
},
],
}
self._set_expected_body(expected['flavors'][0], fakes.FLAVORS['2'])
self.assertEqual(expected, flavor)
def _test_list_flavors_with_invalid_filter(
self, url, expected_exception=exception.ValidationError):
controller_list = self.controller.index
if 'detail' in url:
controller_list = self.controller.detail
req = self._build_request(url)
self.assertRaises(expected_exception,
controller_list, req)
def test_list_flavors_with_invalid_non_int_limit(self):
self._test_list_flavors_with_invalid_filter('/flavors?limit=-9')
def test_list_detail_flavors_with_invalid_non_int_limit(self):
self._test_list_flavors_with_invalid_filter('/flavors/detail?limit=-9')
def test_list_flavors_with_invalid_string_limit(self):
self._test_list_flavors_with_invalid_filter('/flavors?limit=abc')
def test_list_detail_flavors_with_invalid_string_limit(self):
self._test_list_flavors_with_invalid_filter(
'/flavors/detail?limit=abc')
def test_list_duplicate_query_with_invalid_string_limit(self):
self._test_list_flavors_with_invalid_filter(
'/flavors?limit=1&limit=abc')
def test_list_detail_duplicate_query_with_invalid_string_limit(self):
self._test_list_flavors_with_invalid_filter(
'/flavors/detail?limit=1&limit=abc')
def _test_list_flavors_duplicate_query_parameters_validation(
self, url, expected=None):
controller_list = self.controller.index
if 'detail' in url:
controller_list = self.controller.detail
expected_resp = [{
"id": fakes.FLAVORS['2'].flavorid,
"name": fakes.FLAVORS['2'].name,
"links": [
{
"rel": "self",
"href": "http://localhost/v2.1/flavors/2",
},
{
"rel": "bookmark",
"href": "http://localhost/flavors/2",
},
],
}]
if expected:
expected_resp[0].update(expected)
if self.expect_description:
expected_resp[0]['description'] = (
fakes.FLAVORS['2'].description)
if 'detail' in url and self.expect_extra_specs:
expected_resp[0]['extra_specs'] = (
fakes.FLAVORS['2'].extra_specs)
if self.omit_legacy_fields:
expected_resp[0].pop('rxtx_factor', None)
expected_resp[0].pop('OS-FLV-DISABLED:disabled', None)
params = {
'limit': 1,
'marker': 1,
'is_public': 't',
'minRam': 2,
'minDisk': 2,
'sort_key': 'id',
'sort_dir': 'asc'
}
for param, value in params.items():
req = self._build_request(
url + '?marker=1&%s=%s&%s=%s' %
(param, value, param, value))
result = controller_list(req)
self.assertEqual(expected_resp, result['flavors'])
def test_list_duplicate_query_parameters_validation(self):
self._test_list_flavors_duplicate_query_parameters_validation(
'/flavors')
def test_list_detail_duplicate_query_parameters_validation(self):
expected = {
"ram": fakes.FLAVORS['2'].memory_mb,
"disk": fakes.FLAVORS['2'].root_gb,
"vcpus": fakes.FLAVORS['2'].vcpus,
"os-flavor-access:is_public": True,
"rxtx_factor": '',
"OS-FLV-EXT-DATA:ephemeral": fakes.FLAVORS['2'].ephemeral_gb,
"OS-FLV-DISABLED:disabled": fakes.FLAVORS['2'].disabled,
"swap": fakes.FLAVORS['2'].swap
}
self._test_list_flavors_duplicate_query_parameters_validation(
'/flavors/detail', expected)
def _test_list_flavors_with_allowed_filter(
self, url, expected=None, req=None):
controller_list = self.controller.index
if 'detail' in url:
controller_list = self.controller.detail
expected_resp = [{
"id": fakes.FLAVORS['2'].flavorid,
"name": fakes.FLAVORS['2'].name,
"links": [
{
"rel": "self",
"href": "http://localhost/v2.1/flavors/2",
},
{
"rel": "bookmark",
"href": "http://localhost/flavors/2",
},
],
}]
if expected:
expected_resp[0].update(expected)
if self.expect_description:
expected_resp[0]['description'] = (
fakes.FLAVORS['2'].description)
if 'detail' in url and self.expect_extra_specs:
expected_resp[0]['extra_specs'] = (
fakes.FLAVORS['2'].extra_specs)
if self.omit_legacy_fields:
expected_resp[0].pop('rxtx_factor', None)
expected_resp[0].pop('OS-FLV-DISABLED:disabled', None)
req = req or self._build_request(url + '&limit=1&marker=1')
result = controller_list(req)
self.assertEqual(expected_resp, result['flavors'])
def test_list_flavors_with_additional_filter(self):
self._test_list_flavors_with_allowed_filter(
'/flavors?limit=1&marker=1&additional=something')
def test_list_detail_flavors_with_additional_filter(self):
expected = {
"ram": fakes.FLAVORS['2'].memory_mb,
"disk": fakes.FLAVORS['2'].root_gb,
"vcpus": fakes.FLAVORS['2'].vcpus,
"os-flavor-access:is_public": True,
"rxtx_factor": '',
"OS-FLV-EXT-DATA:ephemeral": fakes.FLAVORS['2'].ephemeral_gb,
"OS-FLV-DISABLED:disabled": fakes.FLAVORS['2'].disabled,
"swap": fakes.FLAVORS['2'].swap
}
self._test_list_flavors_with_allowed_filter(
'/flavors/detail?limit=1&marker=1&additional=something',
expected)
def test_list_flavors_with_min_ram_filter_as_negative_int(self):
self._test_list_flavors_with_allowed_filter(
'/flavors?minRam=-2')
def test_list_detail_flavors_with_min_ram_filter_as_negative_int(self):
expected = {
"ram": fakes.FLAVORS['2'].memory_mb,
"disk": fakes.FLAVORS['2'].root_gb,
"vcpus": fakes.FLAVORS['2'].vcpus,
"os-flavor-access:is_public": True,
"rxtx_factor": '',
"OS-FLV-EXT-DATA:ephemeral": fakes.FLAVORS['2'].ephemeral_gb,
"OS-FLV-DISABLED:disabled": fakes.FLAVORS['2'].disabled,
"swap": fakes.FLAVORS['2'].swap
}
self._test_list_flavors_with_allowed_filter(
'/flavors/detail?minRam=-2', expected)
def test_list_flavors_with_min_ram_filter_as_float(self):
self._test_list_flavors_with_invalid_filter(
'/flavors?minRam=1.2', expected_exception=webob.exc.HTTPBadRequest)
def test_list_detail_flavors_with_min_ram_filter_as_float(self):
self._test_list_flavors_with_invalid_filter(
'/flavors/detail?minRam=1.2',
expected_exception=webob.exc.HTTPBadRequest)
def test_list_flavors_with_min_disk_filter_as_negative_int(self):
self._test_list_flavors_with_allowed_filter('/flavors?minDisk=-2')
def test_list_detail_flavors_with_min_disk_filter_as_negative_int(self):
expected = {
"ram": fakes.FLAVORS['2'].memory_mb,
"disk": fakes.FLAVORS['2'].root_gb,
"vcpus": fakes.FLAVORS['2'].vcpus,
"os-flavor-access:is_public": True,
"rxtx_factor": '',
"OS-FLV-EXT-DATA:ephemeral": fakes.FLAVORS['2'].ephemeral_gb,
"OS-FLV-DISABLED:disabled": fakes.FLAVORS['2'].disabled,
"swap": fakes.FLAVORS['2'].swap
}
self._test_list_flavors_with_allowed_filter(
'/flavors/detail?minDisk=-2', expected)
def test_list_flavors_with_min_disk_filter_as_float(self):
self._test_list_flavors_with_invalid_filter(
'/flavors?minDisk=1.2',
expected_exception=webob.exc.HTTPBadRequest)
def test_list_detail_flavors_with_min_disk_filter_as_float(self):
self._test_list_flavors_with_invalid_filter(
'/flavors/detail?minDisk=1.2',
expected_exception=webob.exc.HTTPBadRequest)
def test_list_flavors_with_is_public_filter_as_string_none(self):
self._test_list_flavors_with_allowed_filter(
'/flavors?is_public=none')
def test_list_detail_flavors_with_is_public_filter_as_string_none(self):
expected = {
"ram": fakes.FLAVORS['2'].memory_mb,
"disk": fakes.FLAVORS['2'].root_gb,
"vcpus": fakes.FLAVORS['2'].vcpus,
"os-flavor-access:is_public": True,
"rxtx_factor": '',
"OS-FLV-EXT-DATA:ephemeral": fakes.FLAVORS['2'].ephemeral_gb,
"OS-FLV-DISABLED:disabled": fakes.FLAVORS['2'].disabled,
"swap": fakes.FLAVORS['2'].swap
}
self._test_list_flavors_with_allowed_filter(
'/flavors/detail?is_public=none', expected)
def test_list_flavors_with_is_public_filter_as_valid_bool(self):
self._test_list_flavors_with_allowed_filter(
'/flavors?is_public=false')
def test_list_detail_flavors_with_is_public_filter_as_valid_bool(self):
expected = {
"ram": fakes.FLAVORS['2'].memory_mb,
"disk": fakes.FLAVORS['2'].root_gb,
"vcpus": fakes.FLAVORS['2'].vcpus,
"OS-FLV-EXT-DATA:ephemeral": fakes.FLAVORS['2'].ephemeral_gb,
"os-flavor-access:is_public": True,
"rxtx_factor": '',
"OS-FLV-DISABLED:disabled": fakes.FLAVORS['2'].disabled,
"swap": fakes.FLAVORS['2'].swap
}
self._test_list_flavors_with_allowed_filter(
'/flavors/detail?is_public=false', expected)
def test_list_flavors_with_is_public_filter_as_invalid_string(self):
self._test_list_flavors_with_allowed_filter(
'/flavors?is_public=invalid')
def test_list_detail_flavors_with_is_public_filter_as_invalid_string(self):
expected = {
"ram": fakes.FLAVORS['2'].memory_mb,
"disk": fakes.FLAVORS['2'].root_gb,
"vcpus": fakes.FLAVORS['2'].vcpus,
"os-flavor-access:is_public": True,
"rxtx_factor": '',
"OS-FLV-EXT-DATA:ephemeral": fakes.FLAVORS['2'].ephemeral_gb,
"OS-FLV-DISABLED:disabled": fakes.FLAVORS['2'].disabled,
"swap": fakes.FLAVORS['2'].swap
}
self._test_list_flavors_with_allowed_filter(
'/flavors/detail?is_public=invalid', expected)
class FlavorsTestV255(FlavorsTestV21):
"""Run the same tests as we would for v2.1 but with a description."""
microversion = '2.55'
expect_description = True
class FlavorsTestV261(FlavorsTestV255):
"""Run the same tests as we would for v2.55 but with a extra_specs."""
microversion = '2.61'
expect_extra_specs = True
class FlavorsTestV275(FlavorsTestV261):
microversion = '2.75'
FLAVOR_WITH_NO_SWAP = objects.Flavor(
id=1,
name='flavor 1',
memory_mb=256,
vcpus=1,
root_gb=10,
ephemeral_gb=20,
flavorid='1',
rxtx_factor=1.0,
vcpu_weight=None,
disabled=False,
is_public=True,
swap=0,
description=None,
extra_specs={"key1": "value1", "key2": "value2"}
)
def test_list_flavors_with_additional_filter_old_version(self):
req = fakes.HTTPRequestV21.blank(
'/flavors?limit=1&marker=1&additional=something',
version='2.74', base_url='http://localhost/v2.1')
self._test_list_flavors_with_allowed_filter(
'/flavors?limit=1&marker=1&additional=something',
req=req)
def test_list_detail_flavors_with_additional_filter_old_version(self):
expected = {
"ram": fakes.FLAVORS['2'].memory_mb,
"disk": fakes.FLAVORS['2'].root_gb,
"vcpus": fakes.FLAVORS['2'].vcpus,
"os-flavor-access:is_public": True,
"rxtx_factor": '',
"OS-FLV-EXT-DATA:ephemeral": fakes.FLAVORS['2'].ephemeral_gb,
"OS-FLV-DISABLED:disabled": fakes.FLAVORS['2'].disabled,
"swap": fakes.FLAVORS['2'].swap
}
req = fakes.HTTPRequestV21.blank(
'/flavors?limit=1&marker=1&additional=something',
version='2.74', base_url='http://localhost/v2.1')
self._test_list_flavors_with_allowed_filter(
'/flavors/detail?limit=1&marker=1&additional=something',
expected, req=req)
def _test_list_flavors_with_additional_filter(self, url):
controller_list = self.controller.index
if 'detail' in url:
controller_list = self.controller.detail
req = self._build_request(url)
self.assertRaises(exception.ValidationError,
controller_list, req)
def test_list_flavors_with_additional_filter(self):
self._test_list_flavors_with_additional_filter(
'/flavors?limit=1&marker=1&additional=something')
def test_list_detail_flavors_with_additional_filter(self):
self._test_list_flavors_with_additional_filter(
'/flavors/detail?limit=1&marker=1&additional=something')
@mock.patch('nova.objects.FlavorList.get_all')
def test_list_flavor_detail_default_swap_value_old_version(self, mock_get):
mock_get.return_value = objects.FlavorList(
objects=[self.FLAVOR_WITH_NO_SWAP])
req = fakes.HTTPRequestV21.blank(
'/flavors/detail?limit=1', version='2.74')
response = self.controller.detail(req)
response_list = response["flavors"]
self.assertEqual(response_list[0]['swap'], "")
@mock.patch('nova.objects.Flavor.get_by_flavor_id')
def test_show_flavor_default_swap_value_old_version(self, mock_get):
mock_get.return_value = self.FLAVOR_WITH_NO_SWAP
req = fakes.HTTPRequestV21.blank(
'/flavors/detail?limit=1', version='2.74')
response = self.controller.show(req, 1)
response_list = response["flavor"]
self.assertEqual(response_list['swap'], "")
@mock.patch('nova.objects.FlavorList.get_all')
def test_list_flavor_detail_default_swap_value(self, mock_get):
mock_get.return_value = objects.FlavorList(
objects=[self.FLAVOR_WITH_NO_SWAP])
req = fakes.HTTPRequestV21.blank(
'/flavors/detail?limit=1', version=self.microversion)
response = self.controller.detail(req)
response_list = response["flavors"]
self.assertEqual(response_list[0]['swap'], 0)
@mock.patch('nova.objects.Flavor.get_by_flavor_id')
def test_show_flavor_default_swap_value(self, mock_get):
mock_get.return_value = self.FLAVOR_WITH_NO_SWAP
req = fakes.HTTPRequestV21.blank(
'/flavors/detail?limit=1', version=self.microversion)
response = self.controller.show(req, 1)
response_list = response["flavor"]
self.assertEqual(response_list['swap'], 0)
class FlavorsTestV2102(FlavorsTestV275):
microversion = '2.102'
omit_legacy_fields = True
def test_list_flavors_with_name_filter_old_version(self):
req = fakes.HTTPRequestV21.blank(
'/flavors?name=false', version='2.101')
self.assertRaises(
exception.ValidationError, self.controller.index, req)
def test_list_detail_flavors_with_name_filter_old_version(self):
req = fakes.HTTPRequestV21.blank(
'/flavors/detail?name=false', version='2.101')
self.assertRaises(
exception.ValidationError, self.controller.detail, req)
def test_list_flavors_with_name_filter(self):
req = fakes.HTTPRequestV21.blank(
'/flavors?name=2', version=self.microversion)
actual = self.controller.index(req)
expected = {
'flavors': [
{
'description': 'flavor 2 description',
'id': '2',
'links': [
{
'href': 'http://localhost/v2.1/flavors/2',
'rel': 'self',
},
{
'href': 'http://localhost/flavors/2',
'rel': 'bookmark',
},
],
'name': 'flavor 2',
},
],
}
self.assertEqual(expected, actual)
def test_list_detail_flavors_with_name_filter(self):
req = fakes.HTTPRequestV21.blank(
'/flavors/detail?name=2', version=self.microversion)
actual = self.controller.detail(req)
expected = {
'flavors': [
{
'OS-FLV-EXT-DATA:ephemeral':
fakes.FLAVORS['2'].ephemeral_gb,
'description': fakes.FLAVORS['2'].description,
'disk': fakes.FLAVORS['2'].root_gb,
'extra_specs': {},
'id': '2',
'links': [
{
'href': 'http://localhost/v2.1/flavors/2',
'rel': 'self',
},
{
'href': 'http://localhost/flavors/2',
'rel': 'bookmark',
},
],
'name': fakes.FLAVORS['2'].name,
'os-flavor-access:is_public': True,
'ram': fakes.FLAVORS['2'].memory_mb,
'swap': fakes.FLAVORS['2'].swap,
'vcpus': fakes.FLAVORS['2'].vcpus,
},
],
}
self.assertEqual(expected, actual)
def test_list_detail_flavors_with_additional_filter_old_version(self):
self.omit_legacy_fields = False
super().test_list_detail_flavors_with_additional_filter_old_version()
class DisabledFlavorsWithRealDBTestV21(test.TestCase):
"""Tests that disabled flavors should not be shown nor listed."""
def setUp(self):
super().setUp()
# Add a new disabled type to the list of flavors
self.req = fakes.HTTPRequestV21.blank('/flavors')
self.context = self.req.environ['nova.context']
self.admin_context = context.get_admin_context()
self.disabled_type = self._create_disabled_flavor()
self.addCleanup(self.disabled_type.destroy)
self.flavors = objects.FlavorList.get_all(self.admin_context)
self.controller = flavors.FlavorsController()
def _create_disabled_flavor(self):
flavor = objects.Flavor(context=self.admin_context,
name='foo.disabled', flavorid='10.disabled',
memory_mb=512, vcpus=2, root_gb=1,
ephemeral_gb=0, swap=0, rxtx_factor=1.0,
vcpu_weight=1, disabled=True, is_public=True,
extra_specs={}, projects=[])
flavor.create()
return flavor
def test_index_should_not_list_disabled_flavors_to_user(self):
self.context.is_admin = False
flavor_list = self.controller.index(self.req)['flavors']
api_flavorids = set(f['id'] for f in flavor_list)
db_flavorids = set(i['flavorid'] for i in self.flavors)
disabled_flavorid = str(self.disabled_type['flavorid'])
self.assertIn(disabled_flavorid, db_flavorids)
self.assertEqual(db_flavorids - set([disabled_flavorid]),
api_flavorids)
def test_index_should_list_disabled_flavors_to_admin(self):
self.context.is_admin = True
flavor_list = self.controller.index(self.req)['flavors']
api_flavorids = set(f['id'] for f in flavor_list)
db_flavorids = set(i['flavorid'] for i in self.flavors)
disabled_flavorid = str(self.disabled_type['flavorid'])
self.assertIn(disabled_flavorid, db_flavorids)
self.assertEqual(db_flavorids, api_flavorids)
def test_show_should_include_disabled_flavor_for_user(self):
"""Counterintuitively we should show disabled flavors to all users and
not just admins. The reason is that, when a user performs a server-show
request, we want to be able to display the pretty flavor name ('512 MB
Instance') and not just the flavor-id even if the flavor id has been
marked disabled.
"""
self.context.is_admin = False
flavor = self.controller.show(
self.req, self.disabled_type['flavorid'])['flavor']
self.assertEqual(flavor['name'], self.disabled_type['name'])
def test_show_should_include_disabled_flavor_for_admin(self):
self.context.is_admin = True
flavor = self.controller.show(
self.req, self.disabled_type['flavorid'])['flavor']
self.assertEqual(flavor['name'], self.disabled_type['name'])
class ParseIsPublicTestV21(test.TestCase):
def setUp(self):
super().setUp()
self.controller = flavors.FlavorsController()
def assertPublic(self, expected, is_public):
self.assertIs(expected, self.controller._parse_is_public(is_public),
'%s did not return %s' % (is_public, expected))
def test_None(self):
self.assertPublic(True, None)
def test_truthy(self):
self.assertPublic(True, True)
self.assertPublic(True, 't')
self.assertPublic(True, 'true')
self.assertPublic(True, 'yes')
self.assertPublic(True, '1')
def test_falsey(self):
self.assertPublic(False, False)
self.assertPublic(False, 'f')
self.assertPublic(False, 'false')
self.assertPublic(False, 'no')
self.assertPublic(False, '0')
def test_string_none(self):
self.assertPublic(None, 'none')
self.assertPublic(None, 'None')
def test_other(self):
self.assertRaises(
webob.exc.HTTPBadRequest, self.assertPublic, None, 'other')