Merge "Don't raise exception when get nested stack"

This commit is contained in:
Jenkins 2015-08-20 15:36:59 +00:00 committed by Gerrit Code Review
commit 8caccf0470
12 changed files with 84 additions and 61 deletions

View File

@ -67,6 +67,8 @@ def get_member_names(group):
def get_resource(stack, resource_name, use_indices, key): def get_resource(stack, resource_name, use_indices, key):
nested_stack = stack.nested() nested_stack = stack.nested()
if not nested_stack:
return None
try: try:
if use_indices: if use_indices:
return get_members(stack)[int(resource_name)] return get_members(stack)[int(resource_name)]
@ -79,12 +81,14 @@ def get_resource(stack, resource_name, use_indices, key):
def get_rsrc_attr(stack, key, use_indices, resource_name, *attr_path): def get_rsrc_attr(stack, key, use_indices, resource_name, *attr_path):
resource = get_resource(stack, resource_name, use_indices, key) resource = get_resource(stack, resource_name, use_indices, key)
return resource.FnGetAtt(*attr_path) if resource:
return resource.FnGetAtt(*attr_path)
def get_rsrc_id(stack, key, use_indices, resource_name): def get_rsrc_id(stack, key, use_indices, resource_name):
resource = get_resource(stack, resource_name, use_indices, key) resource = get_resource(stack, resource_name, use_indices, key)
return resource.FnGetRefId() if resource:
return resource.FnGetRefId()
def get_nested_attrs(stack, key, use_indices, *path): def get_nested_attrs(stack, key, use_indices, *path):

View File

@ -15,7 +15,6 @@ from oslo_log import log as logging
from oslo_utils import timeutils from oslo_utils import timeutils
import six import six
from heat.common import exception
from heat.common.i18n import _ from heat.common.i18n import _
from heat.common.i18n import _LE from heat.common.i18n import _LE
from heat.common import param_utils from heat.common import param_utils
@ -210,13 +209,9 @@ def format_stack_resource(resource, detail=True, with_props=False,
rpc_api.RES_REQUIRED_BY: resource.required_by(), rpc_api.RES_REQUIRED_BY: resource.required_by(),
} }
try: if resource.has_nested():
if (hasattr(resource, 'nested') and callable(resource.nested) and res[rpc_api.RES_NESTED_STACK_ID] = dict(
resource.nested() is not None): resource.nested().identifier())
res[rpc_api.RES_NESTED_STACK_ID] = dict(
resource.nested().identifier())
except exception.NotFound:
pass
if resource.stack.parent_resource_name: if resource.stack.parent_resource_name:
res[rpc_api.RES_PARENT_RESOURCE] = resource.stack.parent_resource_name res[rpc_api.RES_PARENT_RESOURCE] = resource.stack.parent_resource_name

View File

@ -403,6 +403,10 @@ class Resource(object):
self.action, self.status, self.action, self.status,
"Failure occurred while waiting.") "Failure occurred while waiting.")
def has_nested(self):
# common resources have not nested, StackResource overrides it
return False
def has_hook(self, hook): def has_hook(self, hook):
# Clear the cache to make sure the data is up to date: # Clear the cache to make sure the data is up to date:
self._data = None self._data = None

View File

@ -12,6 +12,7 @@
# under the License. # under the License.
from requests import exceptions from requests import exceptions
import six
from heat.common import exception from heat.common import exception
from heat.common.i18n import _ from heat.common.i18n import _
@ -89,6 +90,9 @@ class NestedStack(stack_resource.StackResource):
return attributes.select_from_attribute(attribute, path) return attributes.select_from_attribute(attribute, path)
def FnGetRefId(self): def FnGetRefId(self):
if self.nested() is None:
return six.text_type(self.name)
return self.nested().identifier().arn() return self.nested().identifier().arn()
def handle_update(self, json_snippet, tmpl_diff, prop_diff): def handle_update(self, json_snippet, tmpl_diff, prop_diff):

View File

@ -303,17 +303,19 @@ class ResourceGroup(stack_resource.StackResource):
# Now we iterate over the removal policies, and update the blacklist # Now we iterate over the removal policies, and update the blacklist
# with any additional names # with any additional names
rsrc_names = set(current_blacklist) rsrc_names = set(current_blacklist)
for r in self.properties[self.REMOVAL_POLICIES]:
if self.REMOVAL_RSRC_LIST in r: if nested:
# Tolerate string or int list values for r in self.properties[self.REMOVAL_POLICIES]:
for n in r[self.REMOVAL_RSRC_LIST]: if self.REMOVAL_RSRC_LIST in r:
str_n = six.text_type(n) # Tolerate string or int list values
if str_n in nested: for n in r[self.REMOVAL_RSRC_LIST]:
rsrc_names.add(str_n) str_n = six.text_type(n)
continue if str_n in nested:
rsrc = nested.resource_by_refid(str_n) rsrc_names.add(str_n)
if rsrc: continue
rsrc_names.add(rsrc.name) rsrc = nested.resource_by_refid(str_n)
if rsrc:
rsrc_names.add(rsrc.name)
# If the blacklist has changed, update the resource data # If the blacklist has changed, update the resource data
if rsrc_names != set(current_blacklist): if rsrc_names != set(current_blacklist):

View File

@ -114,8 +114,15 @@ class StackResource(resource.Resource):
self.rpc_client().stack_cancel_update(self.context, self.rpc_client().stack_cancel_update(self.context,
stack_identity) stack_identity)
def has_nested(self):
if self.nested() is not None:
return True
return False
def nested(self, force_reload=False, show_deleted=False): def nested(self, force_reload=False, show_deleted=False):
'''Return a Stack object representing the nested (child) stack. '''Return a Stack object representing the nested (child) stack.
if we catch NotFound exception when loading, return None.
:param force_reload: Forces reloading from the DB instead of returning :param force_reload: Forces reloading from the DB instead of returning
the locally cached Stack object the locally cached Stack object
@ -125,13 +132,13 @@ class StackResource(resource.Resource):
self._nested = None self._nested = None
if self._nested is None and self.resource_id is not None: if self._nested is None and self.resource_id is not None:
self._nested = parser.Stack.load(self.context, try:
self.resource_id, self._nested = parser.Stack.load(self.context,
show_deleted=show_deleted, self.resource_id,
force_reload=force_reload) show_deleted=show_deleted,
force_reload=force_reload)
if self._nested is None: except exception.NotFound:
raise exception.NotFound(_("Nested stack not found in DB")) return None
return self._nested return self._nested
@ -322,18 +329,14 @@ class StackResource(resource.Resource):
def _check_status_complete(self, action, show_deleted=False, def _check_status_complete(self, action, show_deleted=False,
cookie=None): cookie=None):
try: nested = self.nested(force_reload=True, show_deleted=show_deleted)
nested = self.nested(force_reload=True, show_deleted=show_deleted) if nested is None:
except exception.NotFound:
if action == resource.Resource.DELETE: if action == resource.Resource.DELETE:
return True return True
# It's possible the engine handling the create hasn't persisted # It's possible the engine handling the create hasn't persisted
# the stack to the DB when we first start polling for state # the stack to the DB when we first start polling for state
return False return False
if nested is None:
return True
if nested.action != action: if nested.action != action:
return False return False
@ -439,11 +442,7 @@ class StackResource(resource.Resource):
''' '''
Delete the nested stack. Delete the nested stack.
''' '''
try: stack = self.nested()
stack = self.nested()
except exception.NotFound:
return
if stack is None: if stack is None:
return return
@ -505,7 +504,11 @@ class StackResource(resource.Resource):
return self._check_status_complete(resource.Resource.CHECK) return self._check_status_complete(resource.Resource.CHECK)
def prepare_abandon(self): def prepare_abandon(self):
return self.nested().prepare_abandon() nested_stack = self.nested()
if nested_stack:
return self.nested().prepare_abandon()
return {}
def get_output(self, op): def get_output(self, op):
''' '''

View File

@ -235,14 +235,10 @@ class Stack(collections.Mapping):
for res in six.itervalues(self): for res in six.itervalues(self):
yield res yield res
get_nested = getattr(res, 'nested', None) if not res.has_nested() or nested_depth == 0:
if not callable(get_nested) or nested_depth == 0:
continue
nested_stack = get_nested()
if nested_stack is None:
continue continue
nested_stack = res.nested()
for nested_res in nested_stack.iter_resources(nested_depth - 1): for nested_res in nested_stack.iter_resources(nested_depth - 1):
yield nested_res yield nested_res
@ -860,7 +856,7 @@ class Stack(collections.Mapping):
def supports_check_action(self): def supports_check_action(self):
def is_supported(stack, res): def is_supported(stack, res):
if hasattr(res, 'nested'): if res.has_nested():
return res.nested().supports_check_action() return res.nested().supports_check_action()
else: else:
return hasattr(res, 'handle_%s' % self.CHECK.lower()) return hasattr(res, 'handle_%s' % self.CHECK.lower())

View File

@ -437,6 +437,7 @@ class HeatScalingGroupAttrTest(common.HeatTestCase):
def test_index_dotted_attribute(self): def test_index_dotted_attribute(self):
mock_members = self.patchobject(grouputils, 'get_members') mock_members = self.patchobject(grouputils, 'get_members')
self.group.nested = mock.Mock()
members = [] members = []
output = [] output = []
for ip_ex in six.moves.range(0, 2): for ip_ex in six.moves.range(0, 2):

View File

@ -254,6 +254,12 @@ class StackResourceType(stack_resource.StackResource, GenericResource):
def handle_delete(self): def handle_delete(self):
self.delete_nested() self.delete_nested()
def has_nested(self):
if self.nested() is not None:
return True
return False
class ResourceWithRestoreType(ResWithComplexPropsAndAttrs): class ResourceWithRestoreType(ResWithComplexPropsAndAttrs):

View File

@ -45,7 +45,8 @@ class FormatTest(common.HeatTestCase):
'generic2': { 'generic2': {
'Type': 'GenericResourceType', 'Type': 'GenericResourceType',
'DependsOn': 'generic1'}, 'DependsOn': 'generic1'},
'generic3': {'Type': 'ResWithShowAttrType'} 'generic3': {'Type': 'ResWithShowAttrType'},
'generic4': {'Type': 'StackResourceType'}
} }
}) })
self.stack = parser.Stack(utils.dummy_context(), 'test_stack', self.stack = parser.Stack(utils.dummy_context(), 'test_stack',
@ -172,7 +173,7 @@ class FormatTest(common.HeatTestCase):
self.assertEqual('', props['a_string']) self.assertEqual('', props['a_string'])
def test_format_stack_resource_with_nested_stack(self): def test_format_stack_resource_with_nested_stack(self):
res = self.stack['generic1'] res = self.stack['generic4']
nested_id = {'foo': 'bar'} nested_id = {'foo': 'bar'}
res.nested = mock.Mock() res.nested = mock.Mock()
res.nested.return_value.identifier.return_value = nested_id res.nested.return_value.identifier.return_value = nested_id
@ -181,7 +182,7 @@ class FormatTest(common.HeatTestCase):
self.assertEqual(nested_id, formatted[rpc_api.RES_NESTED_STACK_ID]) self.assertEqual(nested_id, formatted[rpc_api.RES_NESTED_STACK_ID])
def test_format_stack_resource_with_nested_stack_none(self): def test_format_stack_resource_with_nested_stack_none(self):
res = self.stack['generic1'] res = self.stack['generic4']
res.nested = mock.Mock() res.nested = mock.Mock()
res.nested.return_value = None res.nested.return_value = None
@ -203,9 +204,9 @@ class FormatTest(common.HeatTestCase):
self.assertEqual(resource_keys, set(six.iterkeys(formatted))) self.assertEqual(resource_keys, set(six.iterkeys(formatted)))
def test_format_stack_resource_with_nested_stack_not_found(self): def test_format_stack_resource_with_nested_stack_not_found(self):
res = self.stack['generic1'] res = self.stack['generic4']
res.nested = mock.Mock() self.patchobject(parser.Stack, 'load',
res.nested.side_effect = exception.NotFound() side_effect=exception.NotFound())
resource_keys = set(( resource_keys = set((
rpc_api.RES_CREATION_TIME, rpc_api.RES_CREATION_TIME,
@ -222,10 +223,11 @@ class FormatTest(common.HeatTestCase):
rpc_api.RES_REQUIRED_BY)) rpc_api.RES_REQUIRED_BY))
formatted = api.format_stack_resource(res, False) formatted = api.format_stack_resource(res, False)
# 'nested_stack_id' is not in formatted
self.assertEqual(resource_keys, set(six.iterkeys(formatted))) self.assertEqual(resource_keys, set(six.iterkeys(formatted)))
def test_format_stack_resource_with_nested_stack_empty(self): def test_format_stack_resource_with_nested_stack_empty(self):
res = self.stack['generic1'] res = self.stack['generic4']
nested_id = {'foo': 'bar'} nested_id = {'foo': 'bar'}
res.nested = mock.MagicMock() res.nested = mock.MagicMock()

View File

@ -249,7 +249,7 @@ class StackTest(common.HeatTestCase):
def test_iter_resources(self): def test_iter_resources(self):
tpl = {'HeatTemplateFormatVersion': '2012-12-12', tpl = {'HeatTemplateFormatVersion': '2012-12-12',
'Resources': 'Resources':
{'A': {'Type': 'GenericResourceType'}, {'A': {'Type': 'StackResourceType'},
'B': {'Type': 'GenericResourceType'}}} 'B': {'Type': 'GenericResourceType'}}}
self.stack = stack.Stack(self.ctx, 'test_stack', self.stack = stack.Stack(self.ctx, 'test_stack',
template.Template(tpl), template.Template(tpl),
@ -276,7 +276,7 @@ class StackTest(common.HeatTestCase):
def test_iter_resources_cached(self, mock_drg): def test_iter_resources_cached(self, mock_drg):
tpl = {'HeatTemplateFormatVersion': '2012-12-12', tpl = {'HeatTemplateFormatVersion': '2012-12-12',
'Resources': 'Resources':
{'A': {'Type': 'GenericResourceType'}, {'A': {'Type': 'StackResourceType'},
'B': {'Type': 'GenericResourceType'}}} 'B': {'Type': 'GenericResourceType'}}}
self.stack = stack.Stack(self.ctx, 'test_stack', self.stack = stack.Stack(self.ctx, 'test_stack',
template.Template(tpl), template.Template(tpl),

View File

@ -195,6 +195,11 @@ class StackResourceTest(StackResourceBaseTest):
nest.return_value.prepare_abandon.assert_called_once_with() nest.return_value.prepare_abandon.assert_called_once_with()
self.assertEqual({'X': 'Y'}, ret) self.assertEqual({'X': 'Y'}, ret)
def test_nested_abandon_stack_not_found(self):
self.parent_resource.nested = mock.MagicMock(return_value=None)
ret = self.parent_resource.prepare_abandon()
self.assertEqual({}, ret)
@testtools.skipIf(six.PY3, "needs a separate change") @testtools.skipIf(six.PY3, "needs a separate change")
def test_implementation_signature(self): def test_implementation_signature(self):
self.parent_resource.child_template = mock.Mock( self.parent_resource.child_template = mock.Mock(
@ -464,10 +469,11 @@ class StackResourceTest(StackResourceBaseTest):
parser.Stack.load(self.parent_resource.context, parser.Stack.load(self.parent_resource.context,
self.parent_resource.resource_id, self.parent_resource.resource_id,
show_deleted=False, show_deleted=False,
force_reload=False).AndReturn(None) force_reload=False).AndRaise(
exception.NotFound)
self.m.ReplayAll() self.m.ReplayAll()
self.assertRaises(exception.NotFound, self.parent_resource.nested) self.assertIsNone(self.parent_resource.nested())
self.m.VerifyAll() self.m.VerifyAll()
def test_load_nested_cached(self): def test_load_nested_cached(self):
@ -494,10 +500,10 @@ class StackResourceTest(StackResourceBaseTest):
parser.Stack.load(self.parent_resource.context, parser.Stack.load(self.parent_resource.context,
self.parent_resource.resource_id, self.parent_resource.resource_id,
show_deleted=False, show_deleted=False,
force_reload=True).AndReturn(None) force_reload=True).AndRaise(
exception.NotFound)
self.m.ReplayAll() self.m.ReplayAll()
self.assertRaises(exception.NotFound, self.parent_resource.nested, self.assertIsNone(self.parent_resource.nested(force_reload=True))
force_reload=True)
self.m.VerifyAll() self.m.VerifyAll()
def test_delete_nested_none_nested_stack(self): def test_delete_nested_none_nested_stack(self):