diff --git a/.gitignore b/.gitignore index a54cefc211..88d6e0698e 100644 --- a/.gitignore +++ b/.gitignore @@ -26,6 +26,7 @@ pip-log.txt .tox nosetests.xml .testrepository +*.sqlite # Translations *.mo diff --git a/rally/consts.py b/rally/consts.py index 8d47c56048..aa1d881042 100644 --- a/rally/consts.py +++ b/rally/consts.py @@ -47,6 +47,7 @@ class _TaskStatus(_Immutable, _Enum): REPO_TOOL_GETTING_REPOS = 'repo_tool->getting_repos' + DEPLOY_STARTED = 'deploy->started' DEPLOY_CREATING_VENV = 'deploy->create_venv_to_deploy_openstack' DEPLOY_BUILDING_OPENSTACK_IN_VENV = 'deploy->building_openstack_in_venv' DEPLOY_BUILDING_IMAGE = 'deploy->building_images_with_openstack' diff --git a/rally/deploy/engine.py b/rally/deploy/engine.py index 2995e266a9..93edb0422f 100644 --- a/rally/deploy/engine.py +++ b/rally/deploy/engine.py @@ -17,6 +17,8 @@ import abc +from rally import consts +from rally import db from rally import exceptions from rally import utils @@ -49,11 +51,14 @@ class EngineFactory(object): __metaclass__ = abc.ABCMeta @staticmethod - def get_engine(name, config): + def get_engine(name, uuid, config): """Returns instance of deploy engine with corresponding name.""" for engine in utils.itersubclasses(EngineFactory): if name == engine.__name__: - return engine(config) + new_engine = engine(config) + new_engine.task_uuid = str(uuid) + db.task_create({'uuid': str(uuid)}) + return new_engine raise exceptions.NoSuchEngine(engine_name=name) @staticmethod @@ -69,8 +74,16 @@ class EngineFactory(object): def cleanup(self): """Cleanup OpenStack deployment.""" + def update_status(self, status): + db.task_update(self.task_uuid, {'status': status}) + def __enter__(self): - return self.deploy() + self.update_status(consts.TaskStatus.DEPLOY_STARTED) + deploy = self.deploy() + self.update_status(consts.TaskStatus.DEPLOY_FINISHED) + return deploy def __exit__(self, type, value, traceback): + self.update_status(consts.TaskStatus.CLEANUP) self.cleanup() + self.update_status(consts.TaskStatus.FINISHED) diff --git a/tests/deploy/test_engine_factory.py b/tests/deploy/test_engine_factory.py index 23bef67232..36d56d5220 100644 --- a/tests/deploy/test_engine_factory.py +++ b/tests/deploy/test_engine_factory.py @@ -17,6 +17,9 @@ """Test for deploy engines.""" +import mock +import uuid + from rally import deploy from rally import exceptions from rally import test @@ -26,11 +29,15 @@ class EngineFactoryTestCase(test.NoDBTestCase): def test_get_engine_not_found(self): self.assertRaises(exceptions.NoSuchEngine, - deploy.EngineFactory.get_engine, + deploy.EngineFactory.get_engine, uuid.uuid4(), "non_existing_engine", None) def _create_fake_engines(self): class EngineMixIn(object): + + def __init__(self, config): + pass + def deploy(self): pass @@ -38,25 +45,25 @@ class EngineFactoryTestCase(test.NoDBTestCase): pass class EngineFake1(EngineMixIn, deploy.EngineFactory): - def __init__(self, config): - pass + pass class EngineFake2(EngineMixIn, deploy.EngineFactory): - def __init__(self, config): - pass + pass class EngineFake3(EngineFake2): - def __init__(self, config): - pass + pass return [EngineFake1, EngineFake2, EngineFake3] def test_get_engine(self): engines = self._create_fake_engines() - for e in engines: - engine_inst = deploy.EngineFactory.get_engine(e.__name__, None) - # TODO(boris-42): make it work through assertIsInstance - self.assertEqual(str(type(engine_inst)), str(e)) + with mock.patch('rally.deploy.engine.db'): + for e in engines: + engine_inst = deploy.EngineFactory.get_engine(e.__name__, + uuid.uuid4(), + {}) + # TODO(boris-42): make it work through assertIsInstance + self.assertEqual(str(type(engine_inst)), str(e)) def test_get_available_engines(self): engines = set([e.__name__ for e in self._create_fake_engines()]) @@ -80,7 +87,9 @@ class EngineFactoryTestCase(test.NoDBTestCase): def cleanup(self): self.cleanuped = True - with deploy.EngineFactory.get_engine('A', None) as deployment: - self.assertTrue(deployment.deployed) + with mock.patch('rally.deploy.engine.db'): + with deploy.EngineFactory.get_engine('A', uuid.uuid4(), + None) as deployment: + self.assertTrue(deployment.deployed) self.assertTrue(deployment.cleanuped) diff --git a/tests/deploy/test_fake_engine.py b/tests/deploy/test_fake_engine.py index acadcd5c34..0dfc6538e3 100644 --- a/tests/deploy/test_fake_engine.py +++ b/tests/deploy/test_fake_engine.py @@ -17,6 +17,9 @@ """Test fake deploy engines.""" +import mock +import uuid + from rally import deploy from rally.deploy.engines import fake_engine from rally import test @@ -40,5 +43,7 @@ class TestFakeDeployEngine(test.NoDBTestCase): fake_engine.FakeEngine({}).cleanup() def test_fake_engine_is_in_factory(self): - engine = deploy.EngineFactory.get_engine('FakeEngine', {}) - self.assertIsInstance(engine, fake_engine.FakeEngine) + with mock.patch('rally.db'): + engine = deploy.EngineFactory.get_engine('FakeEngine', + uuid.uuid4(), {}) + self.assertIsInstance(engine, fake_engine.FakeEngine) diff --git a/tests/deploy/test_task_status.py b/tests/deploy/test_task_status.py new file mode 100644 index 0000000000..92823aa1be --- /dev/null +++ b/tests/deploy/test_task_status.py @@ -0,0 +1,92 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +# 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. + +"""Test task status update.""" + +import mock +import uuid + +from rally import consts +from rally import deploy +from rally import test + + +class FakeFailure(Exception): + pass + + +class EngineFailedDeploy(deploy.engines.fake_engine.FakeEngine): + + def deploy(self): + raise FakeFailure('fake deploy failed') + + +class EngineFailedCleanup(deploy.engines.fake_engine.FakeEngine): + + def cleanup(self): + raise FakeFailure('fake deploy failed') + + +get_engine = deploy.EngineFactory.get_engine + + +class TaskStatusTestCase(test.NoDBTestCase): + + def test_task_status_basic_chain(self): + task_uuid = str(uuid.uuid4()) + with mock.patch('rally.deploy.engine.db') as mock_obj: + with get_engine('FakeEngine', task_uuid, {}): + pass + s = consts.TaskStatus + expected = [ + mock.call.task_create({'uuid': task_uuid}), + mock.call.task_update(task_uuid, {'status': s.DEPLOY_STARTED}), + mock.call.task_update(task_uuid, {'status': s.DEPLOY_FINISHED}), + mock.call.task_update(task_uuid, {'status': s.CLEANUP}), + mock.call.task_update(task_uuid, {'status': s.FINISHED}), + ] + self.assertEqual(mock_obj.mock_calls, expected) + + def _test_failure(self, task_uuid, engine, expected_calls): + with mock.patch('rally.deploy.engine.db') as mock_obj: + engine = get_engine(engine, task_uuid, {}) + try: + with engine: + pass + except FakeFailure: + pass + self.assertEqual(mock_obj.mock_calls, expected_calls) + + def test_task_status_failed_deploy(self): + task_uuid = str(uuid.uuid4()) + s = consts.TaskStatus + expected = [ + mock.call.task_create({'uuid': task_uuid}), + mock.call.task_update(task_uuid, {'status': s.DEPLOY_STARTED}), + ] + self._test_failure(task_uuid, 'EngineFailedDeploy', expected) + + def test_task_status_failed_cleanup(self): + task_uuid = str(uuid.uuid4()) + s = consts.TaskStatus + expected = [ + mock.call.task_create({'uuid': task_uuid}), + mock.call.task_update(task_uuid, {'status': s.DEPLOY_STARTED}), + mock.call.task_update(task_uuid, {'status': s.DEPLOY_FINISHED}), + mock.call.task_update(task_uuid, {'status': s.CLEANUP}), + ] + self._test_failure(task_uuid, 'EngineFailedCleanup', expected)