diff --git a/rally/benchmark/scenarios/cinder/utils.py b/rally/benchmark/scenarios/cinder/utils.py index 6b4f993075..d9a943150a 100644 --- a/rally/benchmark/scenarios/cinder/utils.py +++ b/rally/benchmark/scenarios/cinder/utils.py @@ -71,6 +71,6 @@ class CinderScenario(base.Scenario): :param volume: volume object """ volume.delete() - utils.wait_for(volume, is_ready=bench_utils.is_none, - update_resource=bench_utils.get_from_manager(), - timeout=600, check_interval=2) + utils.wait_for_delete(volume, + update_resource=bench_utils.get_from_manager(), + timeout=600, check_interval=2) diff --git a/rally/benchmark/scenarios/nova/utils.py b/rally/benchmark/scenarios/nova/utils.py index 594ca51655..e1af5cfe9a 100644 --- a/rally/benchmark/scenarios/nova/utils.py +++ b/rally/benchmark/scenarios/nova/utils.py @@ -155,9 +155,9 @@ class NovaScenario(base.Scenario): :param server: Server object """ server.delete() - utils.wait_for(server, is_ready=bench_utils.is_none, - update_resource=bench_utils.get_from_manager(), - timeout=600, check_interval=3) + utils.wait_for_delete(server, + update_resource=bench_utils.get_from_manager(), + timeout=600, check_interval=3) @scenario_utils.atomic_action_timer('nova.delete_all_servers') def _delete_all_servers(self): @@ -175,9 +175,9 @@ class NovaScenario(base.Scenario): :param image: Image object """ image.delete() - utils.wait_for(image, is_ready=bench_utils.resource_is("DELETED"), - update_resource=bench_utils.get_from_manager(), - timeout=600, check_interval=3) + utils.wait_for_delete(image, + update_resource=bench_utils.get_from_manager(), + timeout=600, check_interval=3) @scenario_utils.atomic_action_timer('nova.create_image') def _create_image(self, server): diff --git a/rally/benchmark/utils.py b/rally/benchmark/utils.py index 378a8811dd..c61fb78ee6 100644 --- a/rally/benchmark/utils.py +++ b/rally/benchmark/utils.py @@ -39,11 +39,14 @@ def get_from_manager(error_statuses=None): try: resource = resource.manager.get(resource.id) except Exception as e: - if getattr(e, 'http_status', 400) == 404: - return None - raise e - if resource.status.upper() in error_statuses: - raise rally_exceptions.GetResourceFailure(status=resource.status) + if getattr(e, 'code', 400) == 404: + raise rally_exceptions.GetResourceNotFound(status="404") + raise rally_exceptions.GetResourceFailure(status=e) + status = resource.status.upper() + if status == "DELETED": + raise rally_exceptions.GetResourceNotFound(status="404") + if status in error_statuses: + raise rally_exceptions.GetResourceErrorStatus(status=status) return resource return _get_from_manager diff --git a/rally/exceptions.py b/rally/exceptions.py index 057dbb8732..024a44e3a4 100644 --- a/rally/exceptions.py +++ b/rally/exceptions.py @@ -136,8 +136,15 @@ class TimeoutException(RallyException): class GetResourceFailure(RallyException): - msg_fmt = _("Failed to get the resource due to invalid status:" - "`%(status)s`") + msg_fmt = _("Failed to get the resource: '`%(status)s`'") + + +class GetResourceNotFound(GetResourceFailure): + msg_fmt = _("Resource not found: `%(status)s`") + + +class GetResourceErrorStatus(GetResourceFailure): + msg_fmt = _("Resouce has invalid status: `%(status)s`") class SSHError(RallyException): diff --git a/rally/utils.py b/rally/utils.py index 6a04f69117..79813693d7 100644 --- a/rally/utils.py +++ b/rally/utils.py @@ -158,6 +158,31 @@ def wait_for(resource, is_ready, update_resource=None, timeout=60, return resource +def wait_for_delete(resource, update_resource=None, timeout=60, + check_interval=1): + """Waits for the full deletion of resource. + + :param update_resource: Function that should take the resource object + and return an 'updated' resource, or raise + exception rally.exceptions.GetResourceNotFound + that means that resource is deleted. + + :param timeout: Timeout in seconds after which a TimeoutException will be + raised + :param check_interval: Interval in seconds between the two consecutive + readiness checks + """ + start = time.time() + while True: + try: + resource = update_resource(resource) + except exceptions.GetResourceNotFound: + break + time.sleep(check_interval) + if time.time() - start > timeout: + raise exceptions.TimeoutException() + + def log_task_wrapper(log, msg, **kw): """A logging wrapper for any method of a class. diff --git a/tests/benchmark/scenarios/nova/test_utils.py b/tests/benchmark/scenarios/nova/test_utils.py index c38120dca9..019a2d0728 100644 --- a/tests/benchmark/scenarios/nova/test_utils.py +++ b/tests/benchmark/scenarios/nova/test_utils.py @@ -37,6 +37,9 @@ class NovaScenarioTestCase(test.TestCase): self.res_is = mockpatch.Patch(BM_UTILS + ".resource_is") self.get_fm = mockpatch.Patch(BM_UTILS + '.get_from_manager') self.wait_for = mockpatch.Patch(NOVA_UTILS + ".utils.wait_for") + self.wait_for_delete = mockpatch.Patch(NOVA_UTILS + + ".utils.wait_for_delete") + self.useFixture(self.wait_for_delete) self.useFixture(self.wait_for) self.useFixture(self.res_is) self.useFixture(self.get_fm) @@ -111,11 +114,10 @@ class NovaScenarioTestCase(test.TestCase): nova_scenario = utils.NovaScenario() nova_scenario._delete_server(self.server) self.server.delete.assert_called_once_with() - self.wait_for.mock.assert_called_once_with(self.server, - is_ready=mock_isnone, - update_resource=self.gfm(), - check_interval=3, - timeout=600) + self.wait_for_delete.mock.assert_called_once_with( + self.server, + update_resource=self.gfm(), + check_interval=3, timeout=600) self._test_atomic_action_timer(nova_scenario.atomic_actions_time(), 'nova.delete_server') @@ -192,14 +194,12 @@ class NovaScenarioTestCase(test.TestCase): nova_scenario = utils.NovaScenario() nova_scenario._delete_all_servers() expected = [ - mock.call(self.server, is_ready=mock_isnone, - update_resource=self.gfm(), + mock.call(self.server, update_resource=self.gfm(), check_interval=3, timeout=600), - mock.call(self.server1, is_ready=mock_isnone, - update_resource=self.gfm(), + mock.call(self.server1, update_resource=self.gfm(), check_interval=3, timeout=600) ] - self.assertEqual(expected, self.wait_for.mock.mock_calls) + self.assertEqual(expected, self.wait_for_delete.mock.mock_calls) self._test_atomic_action_timer(nova_scenario.atomic_actions_time(), 'nova.delete_all_servers') @@ -207,12 +207,9 @@ class NovaScenarioTestCase(test.TestCase): nova_scenario = utils.NovaScenario() nova_scenario._delete_image(self.image) self.image.delete.assert_called_once_with() - self.wait_for.mock.assert_called_once_with(self.image, - update_resource=self.gfm(), - is_ready=self.res_is.mock(), - check_interval=3, - timeout=600) - self.res_is.mock.assert_has_calls(mock.call('DELETED')) + self.wait_for_delete.mock.assert_called_once_with( + self.image, update_resource=self.gfm(), + check_interval=3, timeout=600) self._test_atomic_action_timer(nova_scenario.atomic_actions_time(), 'nova.delete_image') diff --git a/tests/benchmark/test_utils.py b/tests/benchmark/test_utils.py new file mode 100644 index 0000000000..f73a784c81 --- /dev/null +++ b/tests/benchmark/test_utils.py @@ -0,0 +1,109 @@ +# Copyright 2013: Mirantis Inc. +# 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. + +import mock + +from tests import fakes +from tests import test + +from rally.benchmark import utils +from rally import exceptions + + +class BenchmarkUtilsTestCase(test.TestCase): + + def test_resource_is(self): + is_active = utils.resource_is("ACTIVE") + self.assertTrue(is_active(fakes.FakeResource(status="active"))) + self.assertTrue(is_active(fakes.FakeResource(status="aCtIvE"))) + self.assertFalse(is_active(fakes.FakeResource(status="ERROR"))) + + def test_is_none(self): + self.assertTrue(utils.is_none(None)) + self.assertFalse(utils.is_none(0)) + self.assertFalse(utils.is_none("")) + self.assertFalse(utils.is_none("afafa")) + + def test_false(self): + self.assertFalse(utils.false(None)) + + def test_async_clenaup(self): + cls = mock.MagicMock() + indicies = {} + utils.async_cleanup(cls, indicies) + cls._cleanup_with_clients.assert_called_once_with(indicies) + + def test_infinite_run_args(self): + args = ("a", "b", "c", "d", 123) + for i, real_args in enumerate(utils.infinite_run_args(args)): + self.assertEqual((i,) + args, real_args) + if i == 10: + break + + def test_create_openstack_clients(self): + #TODO(boris-42): Implement this method + pass + + def test_manager_list_sizes(self): + manager = fakes.FakeManager() + + def lst(): + return [1] * 10 + + manager.list = lst + manager_list_size = utils.manager_list_size([5]) + self.assertFalse(manager_list_size(manager)) + + manager_list_size = utils.manager_list_size([10]) + self.assertTrue(manager_list_size(manager)) + + def test_get_from_manager(self): + get_from_manager = utils.get_from_manager() + manager = fakes.FakeManager() + resource = fakes.FakeResource(manager=manager) + manager._cache(resource) + self.assertEqual(get_from_manager(resource), resource) + + def test_get_from_manager_in_error_state(self): + get_from_manager = utils.get_from_manager() + manager = fakes.FakeManager() + resource = fakes.FakeResource(manager=manager, status="ERROR") + manager._cache(resource) + self.assertRaises(exceptions.GetResourceFailure, + get_from_manager, resource) + + def test_get_from_manager_not_found(self): + get_from_manager = utils.get_from_manager() + manager = mock.MagicMock() + resource = fakes.FakeResource(manager=manager, status="ERROR") + + class NotFoundException(Exception): + http_status = 404 + + manager.get = mock.MagicMock(side_effect=NotFoundException) + self.assertRaises(exceptions.GetResourceFailure, + get_from_manager, resource) + + def test_get_from_manager_http_exception(self): + get_from_manager = utils.get_from_manager() + manager = mock.MagicMock() + resource = fakes.FakeResource(manager=manager, status="ERROR") + + class HTTPException(Exception): + pass + + manager.get = mock.MagicMock(side_effect=HTTPException) + self.assertRaises(exceptions.GetResourceFailure, + get_from_manager, resource) diff --git a/tests/fakes.py b/tests/fakes.py index 62fd2336d9..d5db582bbd 100644 --- a/tests/fakes.py +++ b/tests/fakes.py @@ -22,9 +22,9 @@ from rally import utils as rally_utils class FakeResource(object): - def __init__(self, manager=None, name=None): + def __init__(self, manager=None, name=None, status="ACTIVE"): self.name = name or uuid.uuid4() - self.status = "ACTIVE" + self.status = status self.manager = manager self.uuid = uuid.uuid4() self.id = self.uuid