From 2950909d9b871ffb7ac98039d2c6743be620383f Mon Sep 17 00:00:00 2001 From: Pierre Riteau Date: Thu, 29 Aug 2024 11:50:31 +0200 Subject: [PATCH] Add scenario for flavor-based instance reservation This scenario is enabled by default, but the configuration option CONF.resource_reservation.flavor_instance_plugin can be set to False to skip it. Zuul configuration of stable branches are updated to skip this scenario. Change-Id: I54932bb3d74411e324941d295d0241ad3fbae6ac Depends-On: https://review.opendev.org/c/openstack/blazar/+/927413 --- .zuul.yaml | 18 +++ blazar_tempest_plugin/config.py | 3 + .../tests/scenario/test_flavor_reservation.py | 126 ++++++++++++++++++ .../scenario/test_instance_reservation.py | 2 +- 4 files changed, 148 insertions(+), 1 deletion(-) create mode 100644 blazar_tempest_plugin/tests/scenario/test_flavor_reservation.py diff --git a/.zuul.yaml b/.zuul.yaml index 023f6a4..604903e 100644 --- a/.zuul.yaml +++ b/.zuul.yaml @@ -69,15 +69,33 @@ parent: blazar-tempest-plugin-base nodeset: openstack-single-node-jammy override-checkout: stable/2024.1 + vars: + devstack_local_conf: + test-config: + $TEMPEST_CONFIG: + resource_reservation: + flavor_instance_plugin: false - job: name: blazar-tempest-plugin-2023-2 parent: blazar-tempest-plugin-base nodeset: openstack-single-node-jammy override-checkout: stable/2023.2 + vars: + devstack_local_conf: + test-config: + $TEMPEST_CONFIG: + resource_reservation: + flavor_instance_plugin: false - job: name: blazar-tempest-plugin-2023-1 parent: blazar-tempest-plugin-base nodeset: openstack-single-node-jammy override-checkout: stable/2023.1 + vars: + devstack_local_conf: + test-config: + $TEMPEST_CONFIG: + resource_reservation: + flavor_instance_plugin: false diff --git a/blazar_tempest_plugin/config.py b/blazar_tempest_plugin/config.py index 6509bcf..614ddec 100644 --- a/blazar_tempest_plugin/config.py +++ b/blazar_tempest_plugin/config.py @@ -36,6 +36,9 @@ ResourceReservationGroup = [ 'publicURL', 'adminURL', 'internalURL'], help="The endpoint type to use for the resource_reservation " "service."), + cfg.BoolOpt('flavor_instance_plugin', + default=True, + help="Whether to test flavor-based instance reservation"), cfg.IntOpt('lease_interval', default=10, help="Time in seconds between lease status checks."), diff --git a/blazar_tempest_plugin/tests/scenario/test_flavor_reservation.py b/blazar_tempest_plugin/tests/scenario/test_flavor_reservation.py new file mode 100644 index 0000000..fa0d8d0 --- /dev/null +++ b/blazar_tempest_plugin/tests/scenario/test_flavor_reservation.py @@ -0,0 +1,126 @@ +# Copyright 2024 OpenStack Foundation. +# +# 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. + +import datetime + +import testtools + +from oslo_log import log as logging +from tempest.common import waiters +from tempest import config +from tempest.lib import decorators + +from blazar_tempest_plugin.tests.scenario import ( + resource_reservation_scenario as rrs) + +CONF = config.CONF + +LOG = logging.getLogger(__name__) + +# same as the one at blazar/manager/service +LEASE_DATE_FORMAT = "%Y-%m-%d %H:%M" + + +class TestFlavorReservationScenario(rrs.ResourceReservationScenarioTest): + """A scenario that checks the flavor-based instance reservation feature.""" + + def setUp(self): + super(TestFlavorReservationScenario, self).setUp() + # Setup image and flavor for the test instance + # Support both configured and injected values + if not hasattr(self, 'image_ref'): + self.image_ref = CONF.compute.image_ref + if not hasattr(self, 'flavor_ref'): + self.flavor_ref = CONF.compute.flavor_ref + if not self.is_flavor_enough(self.flavor_ref, self.image_ref): + raise self.skipException( + '{image} does not fit in {flavor}'.format( + image=self.image_ref, flavor=self.flavor_ref + ) + ) + self.host = self._add_host_once() + + def tearDown(self): + super(TestFlavorReservationScenario, self).tearDown() + + def get_lease_body(self, lease_name): + current_time = datetime.datetime.utcnow() + end_time = current_time + datetime.timedelta(hours=1) + body = { + "start_date": "now", + "end_date": end_time.strftime(LEASE_DATE_FORMAT), + "name": lease_name, + "events": [], + } + body["reservations"] = [ + { + "resource_type": 'flavor:instance', + 'flavor_id': self.flavor_ref, + 'amount': 1, + 'affinity': None, + 'resource_properties': '', + } + ] + return body + + @decorators.attr(type='smoke') + @testtools.skipUnless( + CONF.resource_reservation.flavor_instance_plugin, + 'Flavor-based instance reservation tests are disabled.') + def test_flavor_instance_reservation(self): + body = self.get_lease_body('flavor-instance-scenario') + lease = self.reservation_client.create_lease(body)['lease'] + reservation = next(iter(lease['reservations'])) + + self.wait_for_lease_status(lease['id'], 'ACTIVE') + + # create an instance within the reservation + create_kwargs = { + 'image_id': CONF.compute.image_ref, + 'flavor': reservation['id'], + } + server1 = self.create_server(clients=self.os_admin, + **create_kwargs) + + # create another instance within the reservation, which is expected to + # fail because we are over the reserved capacity + create_kwargs = { + 'image_id': CONF.compute.image_ref, + 'flavor': reservation['id'], + } + server2 = self.create_server(clients=self.os_admin, + wait_until=None, + **create_kwargs) + waiters.wait_for_server_status(self.os_admin.servers_client, + server2['id'], 'ERROR', + raise_on_error=False) + + # create an instance without specifying a reservation, which is + # expected to fail + create_kwargs = { + 'image_id': CONF.compute.image_ref, + 'flavor': CONF.compute.flavor_ref, + } + server3 = self.create_server(clients=self.os_admin, + wait_until=None, + **create_kwargs) + waiters.wait_for_server_status(self.os_admin.servers_client, + server3['id'], 'ERROR', + raise_on_error=False) + + # delete the lease, which should trigger termination of the instance + self.reservation_client.delete_lease(lease['id']) + waiters.wait_for_server_termination(self.os_admin.servers_client, + server1['id']) diff --git a/blazar_tempest_plugin/tests/scenario/test_instance_reservation.py b/blazar_tempest_plugin/tests/scenario/test_instance_reservation.py index 96c2151..7c864b0 100644 --- a/blazar_tempest_plugin/tests/scenario/test_instance_reservation.py +++ b/blazar_tempest_plugin/tests/scenario/test_instance_reservation.py @@ -36,7 +36,7 @@ class TestInstanceReservationScenario(rrs.ResourceReservationScenarioTest): def setUp(self): super(TestInstanceReservationScenario, self).setUp() - # Setup image and flavor the test instance + # Setup image and flavor for the test instance # Support both configured and injected values if not hasattr(self, 'image_ref'): self.image_ref = CONF.compute.image_ref