From 4caf36905cfb36e444b3fec2a9fc168f9f628360 Mon Sep 17 00:00:00 2001 From: Kanagaraj Manickam Date: Wed, 18 Mar 2015 16:44:24 +0530 Subject: [PATCH] Versioned-object StackLock Adds the object-version support for StackLock Implements blueprint versioned-objects Change-Id: I1aab01ca3ab686a81284f1e5afe05b2b5720a9ea --- heat/engine/stack_lock.py | 18 ++++--- heat/objects/stack_lock.py | 47 +++++++++++++++++ heat/tests/test_engine_service.py | 10 ++-- heat/tests/test_stack_lock.py | 88 ++++++++++++++++++------------- 4 files changed, 115 insertions(+), 48 deletions(-) create mode 100644 heat/objects/stack_lock.py diff --git a/heat/engine/stack_lock.py b/heat/engine/stack_lock.py index 8c9b49634a..5633e4fc54 100644 --- a/heat/engine/stack_lock.py +++ b/heat/engine/stack_lock.py @@ -23,7 +23,8 @@ from heat.common import exception from heat.common.i18n import _LI from heat.common.i18n import _LW from heat.common import messaging as rpc_messaging -from heat.db import api as db_api +from heat.objects import stack_lock as stack_lock_object + cfg.CONF.import_opt('engine_life_check_timeout', 'heat.common.config') @@ -58,7 +59,8 @@ class StackLock(object): Try to acquire a stack lock, but don't raise an ActionInProgress exception or try to steal lock. """ - return db_api.stack_lock_create(self.stack.id, self.engine_id) + return stack_lock_object.StackLock.create(self.stack.id, + self.engine_id) def acquire(self, retry=True): """ @@ -67,8 +69,8 @@ class StackLock(object): :param retry: When True, retry if lock was released while stealing. :type retry: boolean """ - lock_engine_id = db_api.stack_lock_create(self.stack.id, - self.engine_id) + lock_engine_id = stack_lock_object.StackLock.create(self.stack.id, + self.engine_id) if lock_engine_id is None: LOG.debug("Engine %(engine)s acquired lock on stack " "%(stack)s" % {'engine': self.engine_id, @@ -87,8 +89,9 @@ class StackLock(object): "%(engine)s will attempt to steal the lock"), {'stack': self.stack.id, 'engine': self.engine_id}) - result = db_api.stack_lock_steal(self.stack.id, lock_engine_id, - self.engine_id) + result = stack_lock_object.StackLock.steal(self.stack.id, + lock_engine_id, + self.engine_id) if result is None: LOG.info(_LI("Engine %(engine)s successfully stole the lock " @@ -116,7 +119,8 @@ class StackLock(object): def release(self, stack_id): """Release a stack lock.""" # Only the engine that owns the lock will be releasing it. - result = db_api.stack_lock_release(stack_id, self.engine_id) + result = stack_lock_object.StackLock.release(stack_id, + self.engine_id) if result is True: LOG.warn(_LW("Lock was already released on stack %s!"), stack_id) else: diff --git a/heat/objects/stack_lock.py b/heat/objects/stack_lock.py new file mode 100644 index 0000000000..213b3ac708 --- /dev/null +++ b/heat/objects/stack_lock.py @@ -0,0 +1,47 @@ +# +# 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. + + +""" +StackLock object +""" + +from oslo_versionedobjects import base +from oslo_versionedobjects import fields + +from heat.db import api as db_api + + +class StackLock(base.VersionedObject, + base.VersionedObjectDictCompat, + base.ComparableVersionedObject): + fields = { + 'engine_id': fields.StringField(nullable=True), + 'stack_id': fields.StringField(), + 'created_at': fields.DateTimeField(read_only=True), + 'updated_at': fields.DateTimeField(nullable=True), + } + + @classmethod + def create(cls, stack_id, engine_id): + return db_api.stack_lock_create(stack_id, engine_id) + + @classmethod + def steal(cls, stack_id, old_engine_id, new_engine_id): + return db_api.stack_lock_steal(stack_id, + old_engine_id, + new_engine_id) + + @classmethod + def release(cls, stack_id, engine_id): + return db_api.stack_lock_release(stack_id, engine_id) diff --git a/heat/tests/test_engine_service.py b/heat/tests/test_engine_service.py index dd3e1b3180..cad96f533e 100644 --- a/heat/tests/test_engine_service.py +++ b/heat/tests/test_engine_service.py @@ -30,7 +30,6 @@ from heat.common import exception from heat.common import identifier from heat.common import service_utils from heat.common import template_format -from heat.db import api as db_api from heat.engine.clients.os import glance from heat.engine.clients.os import keystone from heat.engine.clients.os import nova @@ -53,6 +52,7 @@ from heat.objects import resource as resource_objects from heat.objects import service as service_objects from heat.objects import software_deployment as software_deployment_object from heat.objects import stack as stack_object +from heat.objects import stack_lock as stack_lock_object from heat.objects import watch_data as watch_data_object from heat.objects import watch_rule as watch_rule_object from heat.openstack.common import threadgroup @@ -886,7 +886,7 @@ class StackServiceCreateUpdateDeleteTest(common.HeatTestCase): sid = stack.store() # Insert a fake lock into the db - db_api.stack_lock_create(stack.id, self.man.engine_id) + stack_lock_object.StackLock.create(stack.id, self.man.engine_id) # Create a fake ThreadGroup too self.man.thread_group_mgr.groups[stack.id] = DummyThreadGroup() @@ -915,7 +915,7 @@ class StackServiceCreateUpdateDeleteTest(common.HeatTestCase): sid = stack.store() # Insert a fake lock into the db - db_api.stack_lock_create(stack.id, "other-engine-fake-uuid") + stack_lock_object.StackLock.create(stack.id, "other-engine-fake-uuid") st = stack_object.Stack.get_by_id(self.ctx, sid) self.m.StubOutWithMock(parser.Stack, 'load') @@ -948,7 +948,7 @@ class StackServiceCreateUpdateDeleteTest(common.HeatTestCase): sid = stack.store() # Insert a fake lock into the db - db_api.stack_lock_create(stack.id, "other-engine-fake-uuid") + stack_lock_object.StackLock.create(stack.id, "other-engine-fake-uuid") st = stack_object.Stack.get_by_id(self.ctx, sid) self.m.StubOutWithMock(parser.Stack, 'load') @@ -980,7 +980,7 @@ class StackServiceCreateUpdateDeleteTest(common.HeatTestCase): sid = stack.store() # Insert a fake lock into the db - db_api.stack_lock_create(stack.id, "other-engine-fake-uuid") + stack_lock_object.StackLock.create(stack.id, "other-engine-fake-uuid") st = stack_object.Stack.get_by_id(self.ctx, sid) self.m.StubOutWithMock(parser.Stack, 'load') diff --git a/heat/tests/test_stack_lock.py b/heat/tests/test_stack_lock.py index 7352296976..5caeb55bc3 100644 --- a/heat/tests/test_stack_lock.py +++ b/heat/tests/test_stack_lock.py @@ -15,8 +15,8 @@ import mock import oslo_messaging as messaging from heat.common import exception -from heat.db import api as db_api from heat.engine import stack_lock +from heat.objects import stack_lock as stack_lock_object from heat.tests import common from heat.tests import utils @@ -35,7 +35,8 @@ class StackLockTest(common.HeatTestCase): pass def test_successful_acquire_new_lock(self): - mock_create = self.patchobject(db_api, 'stack_lock_create', + mock_create = self.patchobject(stack_lock_object.StackLock, + 'create', return_value=None) slock = stack_lock.StackLock(self.context, self.stack, self.engine_id) @@ -44,7 +45,8 @@ class StackLockTest(common.HeatTestCase): mock_create.assert_called_once_with(self.stack.id, self.engine_id) def test_failed_acquire_existing_lock_current_engine(self): - mock_create = self.patchobject(db_api, 'stack_lock_create', + mock_create = self.patchobject(stack_lock_object.StackLock, + 'create', return_value=self.engine_id) slock = stack_lock.StackLock(self.context, self.stack, self.engine_id) @@ -53,9 +55,11 @@ class StackLockTest(common.HeatTestCase): mock_create.assert_called_once_with(self.stack.id, self.engine_id) def test_successful_acquire_existing_lock_engine_dead(self): - mock_create = self.patchobject(db_api, 'stack_lock_create', + mock_create = self.patchobject(stack_lock_object.StackLock, + 'create', return_value='fake-engine-id') - mock_steal = self.patchobject(db_api, 'stack_lock_steal', + mock_steal = self.patchobject(stack_lock_object.StackLock, + 'steal', return_value=None) slock = stack_lock.StackLock(self.context, self.stack, self.engine_id) @@ -67,7 +71,8 @@ class StackLockTest(common.HeatTestCase): self.engine_id) def test_failed_acquire_existing_lock_engine_alive(self): - mock_create = self.patchobject(db_api, 'stack_lock_create', + mock_create = self.patchobject(stack_lock_object.StackLock, + 'create', return_value='fake-engine-id') slock = stack_lock.StackLock(self.context, self.stack, self.engine_id) @@ -77,9 +82,11 @@ class StackLockTest(common.HeatTestCase): mock_create.assert_called_once_with(self.stack.id, self.engine_id) def test_failed_acquire_existing_lock_engine_dead(self): - mock_create = self.patchobject(db_api, 'stack_lock_create', + mock_create = self.patchobject(stack_lock_object.StackLock, + 'create', return_value='fake-engine-id') - mock_steal = self.patchobject(db_api, 'stack_lock_steal', + mock_steal = self.patchobject(stack_lock_object.StackLock, + 'steal', return_value='fake-engine-id2') slock = stack_lock.StackLock(self.context, self.stack, self.engine_id) @@ -91,9 +98,11 @@ class StackLockTest(common.HeatTestCase): self.engine_id) def test_successful_acquire_with_retry(self): - mock_create = self.patchobject(db_api, 'stack_lock_create', + mock_create = self.patchobject(stack_lock_object.StackLock, + 'create', return_value='fake-engine-id') - mock_steal = self.patchobject(db_api, 'stack_lock_steal', + mock_steal = self.patchobject(stack_lock_object.StackLock, + 'steal', side_effect=[True, None]) slock = stack_lock.StackLock(self.context, self.stack, self.engine_id) @@ -106,9 +115,11 @@ class StackLockTest(common.HeatTestCase): [mock.call(self.stack.id, 'fake-engine-id', self.engine_id)] * 2) def test_failed_acquire_one_retry_only(self): - mock_create = self.patchobject(db_api, 'stack_lock_create', + mock_create = self.patchobject(stack_lock_object.StackLock, + 'create', return_value='fake-engine-id') - mock_steal = self.patchobject(db_api, 'stack_lock_steal', + mock_steal = self.patchobject(stack_lock_object.StackLock, + 'steal', return_value=True) slock = stack_lock.StackLock(self.context, self.stack, self.engine_id) @@ -121,68 +132,73 @@ class StackLockTest(common.HeatTestCase): [mock.call(self.stack.id, 'fake-engine-id', self.engine_id)] * 2) def test_thread_lock_context_mgr_exception_acquire_success(self): - db_api.stack_lock_create = mock.Mock(return_value=None) - db_api.stack_lock_release = mock.Mock(return_value=None) + stack_lock_object.StackLock.create = mock.Mock(return_value=None) + stack_lock_object.StackLock.release = mock.Mock(return_value=None) slock = stack_lock.StackLock(self.context, self.stack, self.engine_id) def check_thread_lock(): with slock.thread_lock(self.stack.id): - self.assertEqual(1, db_api.stack_lock_create.call_count) + self.assertEqual(1, + stack_lock_object.StackLock.create.call_count) raise self.TestThreadLockException self.assertRaises(self.TestThreadLockException, check_thread_lock) - self.assertEqual(1, db_api.stack_lock_release.call_count) + self.assertEqual(1, stack_lock_object.StackLock.release.call_count) def test_thread_lock_context_mgr_exception_acquire_fail(self): - db_api.stack_lock_create = mock.Mock(return_value=self.engine_id) - db_api.stack_lock_release = mock.Mock() + stack_lock_object.StackLock.create = mock.Mock( + return_value=self.engine_id) + stack_lock_object.StackLock.release = mock.Mock() slock = stack_lock.StackLock(self.context, self.stack, self.engine_id) def check_thread_lock(): with slock.thread_lock(self.stack.id): - self.assertEqual(1, db_api.stack_lock_create.call_count) + self.assertEqual(1, + stack_lock_object.StackLock.create.call_count) raise exception.ActionInProgress self.assertRaises(exception.ActionInProgress, check_thread_lock) - assert not db_api.stack_lock_release.called + assert not stack_lock_object.StackLock.release.called def test_thread_lock_context_mgr_no_exception(self): - db_api.stack_lock_create = mock.Mock(return_value=None) - db_api.stack_lock_release = mock.Mock(return_value=None) + stack_lock_object.StackLock.create = mock.Mock(return_value=None) + stack_lock_object.StackLock.release = mock.Mock(return_value=None) slock = stack_lock.StackLock(self.context, self.stack, self.engine_id) with slock.thread_lock(self.stack.id): - self.assertEqual(1, db_api.stack_lock_create.call_count) - assert not db_api.stack_lock_release.called + self.assertEqual(1, stack_lock_object.StackLock.create.call_count) + assert not stack_lock_object.StackLock.release.called def test_try_thread_lock_context_mgr_exception(self): - db_api.stack_lock_create = mock.Mock(return_value=None) - db_api.stack_lock_release = mock.Mock(return_value=None) + stack_lock_object.StackLock.create = mock.Mock(return_value=None) + stack_lock_object.StackLock.release = mock.Mock(return_value=None) slock = stack_lock.StackLock(self.context, self.stack, self.engine_id) def check_thread_lock(): with slock.try_thread_lock(self.stack.id): - self.assertEqual(1, db_api.stack_lock_create.call_count) + self.assertEqual(1, + stack_lock_object.StackLock.create.call_count) raise self.TestThreadLockException self.assertRaises(self.TestThreadLockException, check_thread_lock) - self.assertEqual(1, db_api.stack_lock_release.call_count) + self.assertEqual(1, stack_lock_object.StackLock.release.call_count) def test_try_thread_lock_context_mgr_no_exception(self): - db_api.stack_lock_create = mock.Mock(return_value=None) - db_api.stack_lock_release = mock.Mock(return_value=None) + stack_lock_object.StackLock.create = mock.Mock(return_value=None) + stack_lock_object.StackLock.release = mock.Mock(return_value=None) slock = stack_lock.StackLock(self.context, self.stack, self.engine_id) with slock.try_thread_lock(self.stack.id): - self.assertEqual(1, db_api.stack_lock_create.call_count) - assert not db_api.stack_lock_release.called + self.assertEqual(1, stack_lock_object.StackLock.create.call_count) + assert not stack_lock_object.StackLock.release.called def test_try_thread_lock_context_mgr_existing_lock(self): - db_api.stack_lock_create = mock.Mock(return_value=1234) - db_api.stack_lock_release = mock.Mock(return_value=None) + stack_lock_object.StackLock.create = mock.Mock(return_value=1234) + stack_lock_object.StackLock.release = mock.Mock(return_value=None) slock = stack_lock.StackLock(self.context, self.stack, self.engine_id) def check_thread_lock(): with slock.try_thread_lock(self.stack.id): - self.assertEqual(1, db_api.stack_lock_create.call_count) + self.assertEqual(1, + stack_lock_object.StackLock.create.call_count) raise self.TestThreadLockException self.assertRaises(self.TestThreadLockException, check_thread_lock) - assert not db_api.stack_lock_release.called + assert not stack_lock_object.StackLock.release.called def test_engine_alive_ok(self): slock = stack_lock.StackLock(self.context, self.stack, self.engine_id)