Merge "Versioned-object StackLock"

This commit is contained in:
Jenkins 2015-03-23 12:49:42 +00:00 committed by Gerrit Code Review
commit c590ddc373
4 changed files with 115 additions and 48 deletions

View File

@ -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:

View File

@ -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)

View File

@ -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')

View File

@ -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)