Evaluate resource conditions in-place
This allows us to parse the conditions only once instead of reparsing them all every time we encountered a resource with a condition. It also allows us to get rid of some of the API surface area and reduces our reliance on some of the rest. Partial-Bug: #1618713 Change-Id: I3c2273722171b9c4cb13ef6588f7b522b6689b1c
This commit is contained in:
parent
edea94800b
commit
9bd13adeea
|
@ -111,14 +111,7 @@ class CfnTemplateBase(template_common.CommonTemplate):
|
|||
def resource_definitions(self, stack):
|
||||
resources = self.t.get(self.RESOURCES) or {}
|
||||
|
||||
def rsrc_defn_item(name, snippet):
|
||||
try:
|
||||
data = self.parse(stack, snippet)
|
||||
self._validate_resource_definition(name, data)
|
||||
except (TypeError, ValueError, KeyError) as ex:
|
||||
msg = six.text_type(ex)
|
||||
raise exception.StackValidationFailed(message=msg)
|
||||
|
||||
def build_rsrc_defn(name, data):
|
||||
depends = data.get(self.RES_DEPENDS_ON)
|
||||
if isinstance(depends, six.string_types):
|
||||
depends = [depends]
|
||||
|
@ -145,13 +138,33 @@ class CfnTemplateBase(template_common.CommonTemplate):
|
|||
if hasattr(self, 'RES_CONDITION'):
|
||||
kwargs['condition'] = data.get(self.RES_CONDITION)
|
||||
|
||||
defn = rsrc_defn.ResourceDefinition(name, **kwargs)
|
||||
return name, defn
|
||||
return rsrc_defn.ResourceDefinition(name, **kwargs)
|
||||
|
||||
return dict(
|
||||
rsrc_defn_item(name, data)
|
||||
for name, data in resources.items() if self.get_res_condition(
|
||||
stack, data, name))
|
||||
conditions = template_common.Conditions(self.conditions(stack))
|
||||
|
||||
def defns():
|
||||
for name, snippet in resources.items():
|
||||
try:
|
||||
data = self.parse(stack, snippet)
|
||||
self._validate_resource_definition(name, data)
|
||||
except (TypeError, ValueError, KeyError) as ex:
|
||||
msg = six.text_type(ex)
|
||||
raise exception.StackValidationFailed(message=msg)
|
||||
|
||||
defn = build_rsrc_defn(name, data)
|
||||
cond_name = defn.condition_name()
|
||||
|
||||
if cond_name is not None:
|
||||
path = '.'.join([self.RESOURCES,
|
||||
name,
|
||||
self.RES_CONDITION])
|
||||
|
||||
if not conditions.is_enabled(cond_name, path):
|
||||
continue
|
||||
|
||||
yield name, defn
|
||||
|
||||
return dict(defns())
|
||||
|
||||
def add_resource(self, definition, name=None):
|
||||
if name is None:
|
||||
|
|
|
@ -229,19 +229,30 @@ class HOTemplate20130523(template_common.CommonTemplate):
|
|||
def resource_definitions(self, stack):
|
||||
resources = self.t.get(self.RESOURCES) or {}
|
||||
|
||||
def rsrc_defn_from_snippet(name, snippet):
|
||||
conditions = template_common.Conditions(self.conditions(stack))
|
||||
|
||||
def defns():
|
||||
for name, snippet in six.iteritems(resources):
|
||||
try:
|
||||
data = self.parse(stack, snippet)
|
||||
self._validate_resource_definition(name, data)
|
||||
except (TypeError, ValueError, KeyError) as ex:
|
||||
msg = six.text_type(ex)
|
||||
raise exception.StackValidationFailed(message=msg)
|
||||
return self.rsrc_defn_from_snippet(name, data)
|
||||
|
||||
return dict(
|
||||
(name, rsrc_defn_from_snippet(name, data))
|
||||
for name, data in resources.items() if self.get_res_condition(
|
||||
stack, data, name))
|
||||
defn = self.rsrc_defn_from_snippet(name, data)
|
||||
cond_name = defn.condition_name()
|
||||
|
||||
if cond_name is not None:
|
||||
path = '.'.join([self.RESOURCES,
|
||||
name,
|
||||
self.RES_CONDITION])
|
||||
if not conditions.is_enabled(cond_name, path):
|
||||
continue
|
||||
|
||||
yield name, defn
|
||||
|
||||
return dict(defns())
|
||||
|
||||
@classmethod
|
||||
def rsrc_defn_from_snippet(cls, name, data):
|
||||
|
|
|
@ -268,6 +268,13 @@ class ResourceDefinitionCore(object):
|
|||
"""Return the external resource id."""
|
||||
return function.resolve(self._external_id)
|
||||
|
||||
def condition_name(self):
|
||||
"""Return the name of the conditional inclusion rule, if any.
|
||||
|
||||
Returns None if the resource is included unconditionally.
|
||||
"""
|
||||
return self._condition
|
||||
|
||||
def render_hot(self):
|
||||
"""Return a HOT snippet for the resource definition."""
|
||||
if self._rendering is None:
|
||||
|
|
|
@ -114,15 +114,6 @@ class CommonTemplate(template.Template):
|
|||
def has_condition_section(self, snippet):
|
||||
return False
|
||||
|
||||
def get_res_condition(self, stack, res_data, res_name):
|
||||
"""Return the value of condition referenced by resource."""
|
||||
|
||||
path = ''
|
||||
if self.has_condition_section(res_data):
|
||||
path = '.'.join([self.RESOURCES, res_name, self.RES_CONDITION])
|
||||
|
||||
return self.get_condition(res_data, stack, path)
|
||||
|
||||
def get_output_condition(self, stack, o_data, o_key):
|
||||
path = '.'.join([self.OUTPUTS, o_key, self.OUTPUT_CONDITION])
|
||||
|
||||
|
@ -158,3 +149,18 @@ class CommonTemplate(template.Template):
|
|||
snippet[self.OUTPUT_VALUE] = None
|
||||
|
||||
return copy_outputs
|
||||
|
||||
|
||||
class Conditions(object):
|
||||
def __init__(self, conditions_dict):
|
||||
self._conditions = conditions_dict
|
||||
|
||||
def is_enabled(self, condition_name, path):
|
||||
if condition_name is None:
|
||||
return True
|
||||
|
||||
if condition_name not in self._conditions:
|
||||
raise exception.InvalidConditionReference(cd=condition_name,
|
||||
path=path)
|
||||
|
||||
return self._conditions[condition_name]
|
||||
|
|
|
@ -32,6 +32,7 @@ from heat.engine import parameters
|
|||
from heat.engine import rsrc_defn
|
||||
from heat.engine import stack
|
||||
from heat.engine import template
|
||||
from heat.engine import template_common
|
||||
from heat.tests import common
|
||||
from heat.tests.openstack.nova import fakes as fakes_nova
|
||||
from heat.tests import utils
|
||||
|
@ -371,19 +372,18 @@ class TestTemplateConditionParser(common.HeatTestCase):
|
|||
def test_get_res_condition_invalid(self):
|
||||
tmpl = copy.deepcopy(self.tmpl)
|
||||
# test condition name is invalid
|
||||
tmpl.t['resources']['r1']['condition'] = 'invalid_cd'
|
||||
stk = stack.Stack(self.ctx, 'test_res_invalid_condition', tmpl)
|
||||
res_snippet = tmpl.t.get('resources')['r1']
|
||||
|
||||
conds = template_common.Conditions(tmpl.conditions(stk))
|
||||
ex = self.assertRaises(exception.InvalidConditionReference,
|
||||
tmpl.get_res_condition,
|
||||
stk, res_snippet, 'r1')
|
||||
conds.is_enabled, 'invalid_cd',
|
||||
'resources.r1.condition')
|
||||
self.assertIn('Invalid condition "invalid_cd" '
|
||||
'(in resources.r1.condition)', six.text_type(ex))
|
||||
# test condition name is not string
|
||||
tmpl.t['resources']['r1']['condition'] = 111
|
||||
ex = self.assertRaises(exception.InvalidConditionReference,
|
||||
tmpl.get_res_condition,
|
||||
stk, res_snippet, 'r1')
|
||||
conds.is_enabled, 111,
|
||||
'resources.r1.condition')
|
||||
self.assertIn('Invalid condition "111" (in resources.r1.condition)',
|
||||
six.text_type(ex))
|
||||
|
||||
|
|
Loading…
Reference in New Issue