From 123357e35b47697681a29ca672121323c0074722 Mon Sep 17 00:00:00 2001 From: Matt Riedemann Date: Fri, 4 Oct 2019 10:24:19 -0400 Subject: [PATCH] Add boot from volume functional test with a huge request While discussing rate limiting, or the lack thereof, in the API the point came up that it's possible to create multiple servers with a large number of BDMs in a single request and it's not rate limited nor is there any optimization in the API for not GETing the same image in the request more than once. This adds a functional test to demonstrate by wrapping the image API GET calls and asserting they are not cached within the request. Related-Bug: #1846777 Change-Id: I1233fbbaebd1750f572432bf74759b94408a17e3 --- .../tests/functional/test_boot_from_volume.py | 66 +++++++++++++++++++ 1 file changed, 66 insertions(+) diff --git a/nova/tests/functional/test_boot_from_volume.py b/nova/tests/functional/test_boot_from_volume.py index 0fae086358..d92c722aa4 100644 --- a/nova/tests/functional/test_boot_from_volume.py +++ b/nova/tests/functional/test_boot_from_volume.py @@ -10,18 +10,24 @@ # License for the specific language governing permissions and limitations # under the License. +import mock import six from nova import context from nova import objects +from nova import test from nova.tests import fixtures as nova_fixtures from nova.tests.functional.api import client as api_client +from nova.tests.functional import fixtures as func_fixtures from nova.tests.functional import integrated_helpers from nova.tests.functional import test_servers +from nova.tests.unit.image import fake as fake_image +from nova.tests.unit import policy_fixture class BootFromVolumeTest(integrated_helpers.InstanceHelperMixin, test_servers.ServersTestBase): + def _get_hypervisor_stats(self): response = self.admin_api.api_get('/os-hypervisors/statistics') return response.body['hypervisor_statistics'] @@ -162,3 +168,63 @@ class BootFromVolumeTest(integrated_helpers.InstanceHelperMixin, self.assertEqual(400, ex.response.status_code) self.assertIn('You specified more local devices than the limit allows', six.text_type(ex)) + + +class BootFromVolumeLargeRequestTest(test.TestCase, + integrated_helpers.InstanceHelperMixin): + + def setUp(self): + super(BootFromVolumeLargeRequestTest, self).setUp() + self.useFixture(policy_fixture.RealPolicyFixture()) + self.useFixture(nova_fixtures.NeutronFixture(self)) + self.useFixture(nova_fixtures.CinderFixture(self)) + self.useFixture(func_fixtures.PlacementFixture()) + self.image_service = fake_image.stub_out_image_service(self) + self.addCleanup(fake_image.FakeImageService_reset) + + self.api = self.useFixture(nova_fixtures.OSAPIFixture( + api_version='v2.1')).admin_api + # The test cases will handle starting compute/conductor/scheduler + # services if they need them. + + def test_boot_from_volume_10_servers_255_volumes(self): + """Create 10 servers with 255 BDMs each using the same image for 200 + of the BDMs and another image for 55 other BDMs. This is a bit silly + but it just shows that it's possible and there is no rate limiting + involved in this type of very heavy request. + """ + # We only care about API performance in this test case so stub out + # conductor to not do anything. + self.useFixture(nova_fixtures.NoopConductorFixture()) + images = self.api.get_images() + image1 = images[0]['id'] + image2 = images[1]['id'] + server = self._build_minimal_create_server_request( + self.api, 'test_boot_from_volume_10_servers_255_volumes') + server.pop('imageRef') + server['min_count'] = 10 + bdms = [] + # Create 200 BDMs using image1 and 55 using image2. + for boot_index in range(255): + image_uuid = image2 if boot_index >= 200 else image1 + bdms.append({ + 'source_type': 'image', + 'destination_type': 'volume', + 'delete_on_termination': True, + 'volume_size': 1, + 'boot_index': boot_index, + 'uuid': image_uuid + }) + server['block_device_mapping_v2'] = bdms + + # Wrap the image service get method to check how many times it was + # called. + with mock.patch('nova.image.api.API.get', + wraps=self.image_service.show) as mock_image_get: + self.api.post_server({'server': server}) + # FIXME(mriedem): Bug 1846777: Assert that there was no caching of + # the GET /v2/images/{image_id} call. The expected count is: + # 2551 = 10 servers * 255 volumes + 1 root bdm check + expected_image_get_count = (server['min_count'] * len(bdms)) + 1 + self.assertEqual(expected_image_get_count, + mock_image_get.call_count)