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):
|
def resource_definitions(self, stack):
|
||||||
resources = self.t.get(self.RESOURCES) or {}
|
resources = self.t.get(self.RESOURCES) or {}
|
||||||
|
|
||||||
def rsrc_defn_item(name, snippet):
|
def build_rsrc_defn(name, data):
|
||||||
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)
|
|
||||||
|
|
||||||
depends = data.get(self.RES_DEPENDS_ON)
|
depends = data.get(self.RES_DEPENDS_ON)
|
||||||
if isinstance(depends, six.string_types):
|
if isinstance(depends, six.string_types):
|
||||||
depends = [depends]
|
depends = [depends]
|
||||||
|
@ -145,13 +138,33 @@ class CfnTemplateBase(template_common.CommonTemplate):
|
||||||
if hasattr(self, 'RES_CONDITION'):
|
if hasattr(self, 'RES_CONDITION'):
|
||||||
kwargs['condition'] = data.get(self.RES_CONDITION)
|
kwargs['condition'] = data.get(self.RES_CONDITION)
|
||||||
|
|
||||||
defn = rsrc_defn.ResourceDefinition(name, **kwargs)
|
return rsrc_defn.ResourceDefinition(name, **kwargs)
|
||||||
return name, defn
|
|
||||||
|
|
||||||
return dict(
|
conditions = template_common.Conditions(self.conditions(stack))
|
||||||
rsrc_defn_item(name, data)
|
|
||||||
for name, data in resources.items() if self.get_res_condition(
|
def defns():
|
||||||
stack, data, name))
|
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):
|
def add_resource(self, definition, name=None):
|
||||||
if name is None:
|
if name is None:
|
||||||
|
|
|
@ -229,19 +229,30 @@ class HOTemplate20130523(template_common.CommonTemplate):
|
||||||
def resource_definitions(self, stack):
|
def resource_definitions(self, stack):
|
||||||
resources = self.t.get(self.RESOURCES) or {}
|
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:
|
try:
|
||||||
data = self.parse(stack, snippet)
|
data = self.parse(stack, snippet)
|
||||||
self._validate_resource_definition(name, data)
|
self._validate_resource_definition(name, data)
|
||||||
except (TypeError, ValueError, KeyError) as ex:
|
except (TypeError, ValueError, KeyError) as ex:
|
||||||
msg = six.text_type(ex)
|
msg = six.text_type(ex)
|
||||||
raise exception.StackValidationFailed(message=msg)
|
raise exception.StackValidationFailed(message=msg)
|
||||||
return self.rsrc_defn_from_snippet(name, data)
|
|
||||||
|
|
||||||
return dict(
|
defn = self.rsrc_defn_from_snippet(name, data)
|
||||||
(name, rsrc_defn_from_snippet(name, data))
|
cond_name = defn.condition_name()
|
||||||
for name, data in resources.items() if self.get_res_condition(
|
|
||||||
stack, data, 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
|
@classmethod
|
||||||
def rsrc_defn_from_snippet(cls, name, data):
|
def rsrc_defn_from_snippet(cls, name, data):
|
||||||
|
|
|
@ -268,6 +268,13 @@ class ResourceDefinitionCore(object):
|
||||||
"""Return the external resource id."""
|
"""Return the external resource id."""
|
||||||
return function.resolve(self._external_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):
|
def render_hot(self):
|
||||||
"""Return a HOT snippet for the resource definition."""
|
"""Return a HOT snippet for the resource definition."""
|
||||||
if self._rendering is None:
|
if self._rendering is None:
|
||||||
|
|
|
@ -114,15 +114,6 @@ class CommonTemplate(template.Template):
|
||||||
def has_condition_section(self, snippet):
|
def has_condition_section(self, snippet):
|
||||||
return False
|
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):
|
def get_output_condition(self, stack, o_data, o_key):
|
||||||
path = '.'.join([self.OUTPUTS, o_key, self.OUTPUT_CONDITION])
|
path = '.'.join([self.OUTPUTS, o_key, self.OUTPUT_CONDITION])
|
||||||
|
|
||||||
|
@ -158,3 +149,18 @@ class CommonTemplate(template.Template):
|
||||||
snippet[self.OUTPUT_VALUE] = None
|
snippet[self.OUTPUT_VALUE] = None
|
||||||
|
|
||||||
return copy_outputs
|
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 rsrc_defn
|
||||||
from heat.engine import stack
|
from heat.engine import stack
|
||||||
from heat.engine import template
|
from heat.engine import template
|
||||||
|
from heat.engine import template_common
|
||||||
from heat.tests import common
|
from heat.tests import common
|
||||||
from heat.tests.openstack.nova import fakes as fakes_nova
|
from heat.tests.openstack.nova import fakes as fakes_nova
|
||||||
from heat.tests import utils
|
from heat.tests import utils
|
||||||
|
@ -371,19 +372,18 @@ class TestTemplateConditionParser(common.HeatTestCase):
|
||||||
def test_get_res_condition_invalid(self):
|
def test_get_res_condition_invalid(self):
|
||||||
tmpl = copy.deepcopy(self.tmpl)
|
tmpl = copy.deepcopy(self.tmpl)
|
||||||
# test condition name is invalid
|
# test condition name is invalid
|
||||||
tmpl.t['resources']['r1']['condition'] = 'invalid_cd'
|
|
||||||
stk = stack.Stack(self.ctx, 'test_res_invalid_condition', tmpl)
|
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,
|
ex = self.assertRaises(exception.InvalidConditionReference,
|
||||||
tmpl.get_res_condition,
|
conds.is_enabled, 'invalid_cd',
|
||||||
stk, res_snippet, 'r1')
|
'resources.r1.condition')
|
||||||
self.assertIn('Invalid condition "invalid_cd" '
|
self.assertIn('Invalid condition "invalid_cd" '
|
||||||
'(in resources.r1.condition)', six.text_type(ex))
|
'(in resources.r1.condition)', six.text_type(ex))
|
||||||
# test condition name is not string
|
# test condition name is not string
|
||||||
tmpl.t['resources']['r1']['condition'] = 111
|
|
||||||
ex = self.assertRaises(exception.InvalidConditionReference,
|
ex = self.assertRaises(exception.InvalidConditionReference,
|
||||||
tmpl.get_res_condition,
|
conds.is_enabled, 111,
|
||||||
stk, res_snippet, 'r1')
|
'resources.r1.condition')
|
||||||
self.assertIn('Invalid condition "111" (in resources.r1.condition)',
|
self.assertIn('Invalid condition "111" (in resources.r1.condition)',
|
||||||
six.text_type(ex))
|
six.text_type(ex))
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue