Fix the definition of has_nested()

Since Iab7a997c0b4d9dcb8ceb3d2f49692216fe611df1 StackResource.has_nested()
returns True unconditionally. However, some parts of the code expect that
if has_nested() returns True, the caller will be able to call nested() and
not get None returned (this was the original definition of has_nested()).
This change restores the definition without the need to load the nested
stack (which is expensive, and the reason for changing this in the first
place).

This also allows the change from the original patch to be a bit tidier,
without a recurrence of the issue it initially caused (bug 1586906) that
was later fixed by I2ededcc57057f43206922b99bb72c43332336814.

Change-Id: Id26dc62b7513853b3f02a656518c59b3ff8d509a
Related-Bug: #1578854
This commit is contained in:
Zane Bitter 2016-06-02 11:41:39 -04:00
parent e4c09815e9
commit 091ed70d53
5 changed files with 21 additions and 18 deletions

View File

@ -343,11 +343,8 @@ def format_stack_resource(resource, detail=True, with_props=False,
rpc_api.RES_REQUIRED_BY: resource.required_by(),
}
try:
res[rpc_api.RES_NESTED_STACK_ID] = dict(
resource.nested_identifier())
except (AttributeError, TypeError):
pass
if resource.has_nested():
res[rpc_api.RES_NESTED_STACK_ID] = dict(resource.nested_identifier())
if resource.stack.parent_resource_name:
res[rpc_api.RES_PARENT_RESOURCE] = resource.stack.parent_resource_name

View File

@ -452,7 +452,14 @@ class Resource(object):
raise
def has_nested(self):
# common resources have not nested, StackResource overrides it
"""Return True if the resource has an existing nested stack.
For most resource types, this will always return False. StackResource
subclasses return True when appropriate. Resource subclasses that may
return True must also provide a nested_identifier() method to return
the identifier of the nested stack, and a nested() method to return a
Stack object for the nested stack.
"""
return False
def has_hook(self, hook):

View File

@ -126,16 +126,16 @@ class StackResource(resource.Resource):
cancel_with_rollback=False)
def nested_identifier(self):
if not self.resource_id:
raise AttributeError()
if self.resource_id is None:
return None
return identifier.HeatIdentifier(
self.context.tenant_id,
self.physical_resource_name(),
self.resource_id)
def has_nested(self):
# This class and its subclasses manage nested stacks
return True
"""Return True if the resource has an existing nested stack."""
return self.resource_id is not None or self._nested is not None
def nested(self):
"""Return a Stack object representing the nested (child) stack.

View File

@ -1061,7 +1061,7 @@ class EngineService(service.Service):
def _n_deleted(stk, deleted):
for rsrc in deleted:
deleted_rsrc = stk.resources.get(rsrc)
if deleted_rsrc.has_nested() and deleted_rsrc.nested():
if deleted_rsrc.has_nested():
nested_stk = deleted_rsrc.nested()
nested_rsrc = nested_stk.resources.keys()
n_fmt = fmt_action_map(
@ -1075,7 +1075,7 @@ class EngineService(service.Service):
def _n_added(stk, added):
for rsrc in added:
added_rsrc = stk.resources.get(rsrc)
if added_rsrc.has_nested() and added_rsrc.nested():
if added_rsrc.has_nested():
nested_stk = added_rsrc.nested()
nested_rsrc = nested_stk.resources.keys()
n_fmt = fmt_action_map(
@ -1088,10 +1088,7 @@ class EngineService(service.Service):
for rsrc in act['updated']:
current_rsrc = current.resources.get(rsrc)
updated_rsrc = updated.resources.get(rsrc)
if (current_rsrc.has_nested()
and current_rsrc.nested()
and updated_rsrc.has_nested()
and updated_rsrc.nested()):
if current_rsrc.has_nested() and updated_rsrc.has_nested():
current_nested = current_rsrc.nested()
updated_nested = updated_rsrc.nested()
update_task = update.StackUpdate(

View File

@ -206,6 +206,8 @@ class FormatTest(common.HeatTestCase):
def test_format_stack_resource_with_nested_stack(self):
res = self.stack['generic4']
nested_id = {'foo': 'bar'}
res.has_nested = mock.Mock()
res.has_nested.return_value = True
res.nested_identifier = mock.Mock()
res.nested_identifier.return_value = nested_id
@ -214,8 +216,6 @@ class FormatTest(common.HeatTestCase):
def test_format_stack_resource_with_nested_stack_none(self):
res = self.stack['generic4']
res.nested = mock.Mock()
res.nested.return_value = None
resource_keys = set((
rpc_api.RES_CREATION_TIME,
@ -261,6 +261,8 @@ class FormatTest(common.HeatTestCase):
res = self.stack['generic4']
nested_id = {'foo': 'bar'}
res.has_nested = mock.Mock()
res.has_nested.return_value = True
res.nested_identifier = mock.Mock()
res.nested_identifier.return_value = nested_id