From 1da0790e0de43ea58233033355936b3eba5cc114 Mon Sep 17 00:00:00 2001 From: Zane Bitter Date: Fri, 21 Jul 2017 21:32:45 -0400 Subject: [PATCH] Respect locks when changing FAILED resource to COMPLETE If a resource is in a FAILED state, but the plugin overrides _needs_update() to not raise UpdateReplace, and also no actual update is required, we simply change the resource's state to UPDATE_COMPLETE. However, since the locking/unlocking steps were merged with the resource state updates in a7376f7494b310e9367ebe5dcb43b432a4053023, in doing so we were ignoring the lock (since at this stage the resource is still unlocked, and we don't need to take a lock). If another update traversal had acquired the lock in the meantime, we would still write to the locked resource. This change adds a new LOCK_RESPECT lock mode to Resource.store(). In this mode the lock is not acquired or released, but an error will be raised if the resource is found to be locked. Change-Id: I8b5cd1e05b88dd13abc13899a73f23810f7e6135 Closes-Bug: #1705794 Related-Bug: #1662585 --- heat/engine/resource.py | 34 ++++++++++++++-------------------- 1 file changed, 14 insertions(+), 20 deletions(-) diff --git a/heat/engine/resource.py b/heat/engine/resource.py index 6abc6b0fab..4440b1fb49 100644 --- a/heat/engine/resource.py +++ b/heat/engine/resource.py @@ -98,8 +98,11 @@ class PollDelay(Exception): class Resource(status.ResourceStatus): BASE_ATTRIBUTES = (SHOW, ) = (attributes.SHOW_ATTR, ) - LOCK_ACTIONS = (LOCK_NONE, LOCK_ACQUIRE, LOCK_RELEASE) = ( - 0, 1, -1) + LOCK_ACTIONS = ( + LOCK_NONE, LOCK_ACQUIRE, LOCK_RELEASE, LOCK_RESPECT, + ) = ( + None, 1, -1, 0, + ) # If True, this resource must be created before it can be referenced. strict_dependency = True @@ -1297,22 +1300,7 @@ class Resource(status.ResourceStatus): if not persist: return - rs = {'current_template_id': self.current_template_id, - 'updated_at': self.updated_time, - 'requires': self.requires} - if not resource_objects.Resource.select_and_update_by_id( - self.context, self.id, rs, expected_engine_id=None, - atomic_key=self._atomic_key): - LOG.info("Resource %s is locked, can't set template", - six.text_type(self)) - LOG.debug('Resource id:%(resource_id)s locked. ' - 'Expected atomic_key:%(atomic_key)s, ' - 'accessing from engine_id:%(engine_id)s', - {'resource_id': self.id, - 'atomic_key': self._atomic_key, - 'engine_id': self._calling_engine_id}) - raise exception.UpdateInProgress(self.name) - self._incr_atomic_key(self._atomic_key) + self.store(lock=self.LOCK_RESPECT) self._calling_engine_id = engine_id registry = new_stack.env.registry @@ -1502,8 +1490,10 @@ class Resource(status.ResourceStatus): status_reason = _('Update status to COMPLETE for ' 'FAILED resource neither update ' 'nor replace.') + lock = (self.LOCK_RESPECT if self.stack.convergence + else self.LOCK_NONE) self.state_set(self.action, self.COMPLETE, - status_reason) + status_reason, lock=lock) return if not self.stack.convergence: @@ -1969,9 +1959,13 @@ class Resource(status.ResourceStatus): if lock == self.LOCK_ACQUIRE: rs['engine_id'] = self._calling_engine_id expected_engine_id = None - else: # self.LOCK_RELEASE + elif lock == self.LOCK_RESPECT: + expected_engine_id = None + elif lock == self.LOCK_RELEASE: expected_engine_id = self._calling_engine_id rs['engine_id'] = None + else: + assert False, "Invalid lock action: %s" % lock if resource_objects.Resource.select_and_update_by_id( self.context, self.id, rs, expected_engine_id, self._atomic_key):