From 8bb95750da87e88ec30185c48d40eed8e2d2d55b Mon Sep 17 00:00:00 2001 From: Masahito Muroi Date: Mon, 24 Sep 2018 14:42:25 +0200 Subject: [PATCH] Implement scenario test for the new instance reservation feature The current instance reservation test scenario is outdated because it was written for the old instance plugin, which was removed in the Ocata release. This patch replaces it with a scenario test for the new instance reservation feature. Change-Id: Id6cb3f3a6471eee70000c42694870609068eba75 Closes-Bug: #1714438 --- .../scenario/resource_reservation_scenario.py | 50 ++++- .../tests/scenario/test_host_reservation.py | 15 -- .../scenario/test_instance_reservation.py | 192 +++++++----------- 3 files changed, 111 insertions(+), 146 deletions(-) diff --git a/blazar_tempest_plugin/tests/scenario/resource_reservation_scenario.py b/blazar_tempest_plugin/tests/scenario/resource_reservation_scenario.py index 6850a5b..075133c 100644 --- a/blazar_tempest_plugin/tests/scenario/resource_reservation_scenario.py +++ b/blazar_tempest_plugin/tests/scenario/resource_reservation_scenario.py @@ -95,17 +95,49 @@ class ResourceReservationScenarioTest(manager.ScenarioTest): message = "Timed out waiting for lease to change status to DONE" raise exceptions.TimeoutException(message) - def remove_image_snapshot(self, image_name): - try: - image = [i for i in self.image_client.list() - if ['name'] == image_name] - self.image_client.delete(image) - except Exception as e: - LOG.info("Unable to delete %(image_name)s snapshot. " - "Exception: %(message)s", - {'image_name': image_name, 'message': str(e)}) + def wait_for_lease_status(self, lease_id, status): + + def check_lease_status(): + try: + lease = self.reservation_client.get_lease(lease_id)['lease'] + if lease and lease['status'] == status: + return True + else: + LOG.info("Lease with id %s is not %s, but %s", + lease_id, status, lease['status']) + except Exception as e: + LOG.info("Unable to find lease with id %(lease_id)s. " + "Exception: %(message)s", + {'lease_id': lease_id, 'message': str(e)}) + return False + + if not test_utils.call_until_true( + check_lease_status, + CONF.resource_reservation.lease_end_timeout, + CONF.resource_reservation.lease_interval): + message = ("Timed out waiting for lease to change status " + "to %s" % status) + raise exceptions.TimeoutException(message) def is_flavor_enough(self, flavor_id, image_id): image = self.compute_images_client.show_image(image_id)['image'] flavor = self.flavors_client.show_flavor(flavor_id)['flavor'] return image['minDisk'] <= flavor['disk'] + + def fetch_one_compute_host(self): + """Returns the first host listed in nova-compute services.""" + compute = next(iter(self.os_admin.services_client. + list_services(binary='nova-compute')['services'])) + return compute + + def _add_host_once(self): + host = self.fetch_one_compute_host() + hosts = self.reservation_client.list_host()['hosts'] + try: + # TODO(masahito): Fix the check not to depend on '0' fixed check. + # When the scenario test covers the monitor resource feature, there + # are multiple compute hosts. + [h for h in hosts if h['hypervisor_hostname'] == host['host']][0] + except IndexError: + self.reservation_client.create_host({'name': host['host']}) + return host diff --git a/blazar_tempest_plugin/tests/scenario/test_host_reservation.py b/blazar_tempest_plugin/tests/scenario/test_host_reservation.py index 41c3505..cdf87dc 100644 --- a/blazar_tempest_plugin/tests/scenario/test_host_reservation.py +++ b/blazar_tempest_plugin/tests/scenario/test_host_reservation.py @@ -43,12 +43,6 @@ class TestHostReservationScenario(rrs.ResourceReservationScenarioTest): def tearDown(self): super(TestHostReservationScenario, self).tearDown() - def fetch_one_compute_host(self): - """Returns a first host listed in nova-compute services.""" - compute = next(iter(self.os_admin.services_client. - list_services(binary='nova-compute')['services'])) - return compute - def get_lease_body(self, lease_name, host_name): current_time = datetime.datetime.utcnow() end_time = current_time + datetime.timedelta(hours=1) @@ -158,15 +152,6 @@ class TestHostReservationScenario(rrs.ResourceReservationScenarioTest): % (host_name, aggregate_name)) raise exceptions.NotFound(err_msg) - def _add_host_once(self): - host = self.fetch_one_compute_host() - hosts = self.reservation_client.list_host()['hosts'] - try: - [h for h in hosts if h['hypervisor_hostname'] == host['host']][0] - except IndexError: - self.reservation_client.create_host({'name': host['host']}) - return host - @decorators.attr(type='smoke') def test_host_reservation(self): diff --git a/blazar_tempest_plugin/tests/scenario/test_instance_reservation.py b/blazar_tempest_plugin/tests/scenario/test_instance_reservation.py index 4673fdd..4486080 100644 --- a/blazar_tempest_plugin/tests/scenario/test_instance_reservation.py +++ b/blazar_tempest_plugin/tests/scenario/test_instance_reservation.py @@ -15,13 +15,9 @@ import datetime -import dateutil.parser from oslo_log import log as logging -from oslo_serialization import jsonutils as json -from tempest.common import utils from tempest.common import waiters from tempest import config -from tempest import exceptions from tempest.lib import decorators from blazar_tempest_plugin.tests.scenario import ( @@ -33,25 +29,10 @@ LOG = logging.getLogger(__name__) # same as the one at blazar/manager/service LEASE_DATE_FORMAT = "%Y-%m-%d %H:%M" -LEASE_MIN_DURATION = 2 -# TODO(cmart): LEASE_IMAGE_PREFIX should be extracted from CONF -LEASE_IMAGE_PREFIX = 'reserved_' class TestInstanceReservationScenario(rrs.ResourceReservationScenarioTest): - - """Test that checks the instance reservation scenario. - - The following is the scenario outline: - 1) Create an instance with the hint parameters - 2) check vm was shelved - 3) check vm became active - 4) check that a new lease is created on blazar - 5) check its param - 6) wait lease end - 7) make sure VM was snapshoted and removed - - """ + """A scenario test class that checks the instance reservation feature.""" def setUp(self): super(TestInstanceReservationScenario, self).setUp() @@ -67,115 +48,82 @@ class TestInstanceReservationScenario(rrs.ResourceReservationScenarioTest): image=self.image_ref, flavor=self.flavor_ref ) ) + self.host = self._add_host_once() def tearDown(self): super(TestInstanceReservationScenario, self).tearDown() - def add_keypair(self): - self.keypair = self.create_keypair() + 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": 'virtual:instance', + 'vcpus': 1, + 'memory_mb': 1024, + 'disk_gb': 10, + 'amount': 1, + 'affinity': False, + 'resource_properties': '', + } + ] + return body - def boot_server_with_lease_data(self, lease_data, wait): - self.add_keypair() + @decorators.attr(type='smoke') + def test_instance_reservation(self): + body = self.get_lease_body('instance-scenario') + lease = self.reservation_client.create_lease(body)['lease'] + reservation = next(iter(lease['reservations'])) - # Create server with lease_data + self.wait_for_lease_status(lease['id'], 'ACTIVE') + + # create an instance within the reservation create_kwargs = { - 'key_name': self.keypair['name'], - 'scheduler_hints': lease_data - } + 'image_id': CONF.compute.image_ref, + 'flavor': reservation['id'], + 'scheduler_hints': { + 'group': reservation['server_group_id'] + }, + } + server1 = self.create_server(clients=self.os_admin, + **create_kwargs) - server = self.create_server(image_id=self.image_ref, - flavor=self.flavor_ref, - wait_until=wait, - **create_kwargs) - self.server_id = server['id'] - self.server_name = server['name'] + # 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'], + 'scheduler_hints': { + 'group': reservation['server_group_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) - def check_lease_creation(self, expected_lease_data): - server = self.servers_client.show_server(self.server_id)['server'] - expected_lease_params = json.loads(expected_lease_data['lease_params']) + # 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) - # compare lease_data with data passed as parameter - lease = self.get_lease_by_name(expected_lease_params['name']) - - # check lease dates!! (Beware of date format) - lease_start_date = dateutil.parser.parse(lease['start_date']) - lease_start_date = lease_start_date.strftime(LEASE_DATE_FORMAT) - lease_end_date = dateutil.parser.parse(lease['end_date']) - lease_end_date = lease_end_date.strftime(LEASE_DATE_FORMAT) - - self.assertEqual(expected_lease_params['start'], lease_start_date) - self.assertEqual(expected_lease_params['end'], lease_end_date) - - # check lease events! - events = lease['events'] - self.assertTrue(len(events) >= 3) - - self.assertFalse( - len([evt for evt in events if evt['event_type'] != 'start_lease' - and evt['event_type'] != 'end_lease' - and evt['event_type'] != 'before_end_lease']) > 0) - - # check that only one reservation was made and it is for a vm - # compare the resource id from the lease with the server.id attribute! - reservations = lease['reservations'] - self.assertTrue(len(reservations) == 1) - self.assertEqual(server['id'], reservations[0]['resource_id']) - self.assertEqual("virtual:instance", - lease['reservations'][0]['resource_type']) - - def check_server_is_snapshoted(self): - image_name = LEASE_IMAGE_PREFIX + self.server_name - try: - images_list = self.image_client.list() - self.assertNotEmpty( - [image for image in images_list if image.name == image_name]) - except Exception as e: - message = ("Unable to find image with name '%s'. " - "Exception: %s" % (image_name, str(e))) - raise exceptions.NotFound(message) - - def check_server_status(self, expected_status): - server = self.servers_client.show_server(self.server_id)['server'] - self.assertEqual(expected_status, server['status']) - - # TODO(cmart): add blazar to services after pushing this code into tempest - @decorators.skip_because('Instance reservation is not supported yet.', - bug='1659200') - @decorators.attr(type='slow') - @utils.services('compute', 'network') - def test_server_basic_resource_reservation_operation(self): - start_date = datetime.datetime.utcnow() + datetime.timedelta(minutes=1) - end_date = start_date + datetime.timedelta(minutes=LEASE_MIN_DURATION) - start_date = start_date.strftime(LEASE_DATE_FORMAT) - end_date = end_date.strftime(LEASE_DATE_FORMAT) - lease_name = 'scenario_test' - lease_data = { - 'lease_params': '{"name": "%s",' - '"start": "%s",' - '"end": "%s"}' - % (lease_name, start_date, end_date)} - - # boot the server and don't wait until it is active - self.boot_server_with_lease_data(lease_data, wait=False) - self.check_server_status('SHELVED_OFFLOADED') - - # now, wait until the server is active - waiters.wait_for_server_status(self.servers_client, - self.server_id, 'ACTIVE') - self.check_lease_creation(lease_data) - - # wait for lease end - created_lease = self.get_lease_by_name(lease_name) - self.wait_for_lease_end(created_lease['id']) - - # check server final status - self.check_server_is_snapshoted() - waiters.wait_for_server_termination(self.servers_client, - self.server_id) - - # remove created snapshot - image_name = LEASE_IMAGE_PREFIX + self.server_name - self.remove_image_snapshot(image_name) - - # remove created lease - self.delete_lease(created_lease['id']) + # 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'])