Use rsrc_defn_from_snippet for ResourceGroup

This uses resource_defn_from_snippet in build_resource_defintion.
This also changes build_resource_defintion for dependent
Software/Strcutured DeploymentGroup. Also uses scaling library
make_template to build the template in _assemble_nested.

Change-Id: I548b75ea30cf79706e247beba4c4eb4449101742
This commit is contained in:
Rabi Mishra 2015-09-16 23:52:41 +05:30
parent c9140ff1df
commit 8df756e150
7 changed files with 165 additions and 158 deletions

View File

@ -16,7 +16,6 @@ import copy
import itertools
import six
import six.moves
from heat.common import exception
from heat.common import grouputils
@ -24,19 +23,14 @@ from heat.common.i18n import _
from heat.common import timeutils
from heat.engine import attributes
from heat.engine import constraints
from heat.engine.hot import template
from heat.engine import properties
from heat.engine.resources import stack_resource
from heat.engine import rsrc_defn
from heat.engine import scheduler
from heat.engine import support
from heat.engine import template
from heat.scaling import rolling_update
from heat.scaling import template as scale_template
template_template = {
"heat_template_version": "2015-04-30",
"resources": {}
}
from heat.scaling import template as scl_template
class ResourceGroup(stack_resource.StackResource):
@ -277,8 +271,8 @@ class ResourceGroup(stack_resource.StackResource):
return
test_tmpl = self._assemble_nested(["0"], include_all=True)
val_templ = template.Template(test_tmpl)
res_def = val_templ.resource_definitions(self.stack)["0"]
res_def = next(six.itervalues(
test_tmpl.resource_definitions(self.stack)))
# make sure we can resolve the nested resource type
try:
self.stack.env.get_class(res_def.resource_type)
@ -348,8 +342,8 @@ class ResourceGroup(stack_resource.StackResource):
size)
def _get_resources(self):
"""Get templates for resources."""
return [(resource.name, resource.t.render_hot())
"""Get definitions for resources."""
return [(resource.name, resource.t)
for resource in grouputils.get_members(self)]
def _count_black_listed(self):
@ -448,17 +442,28 @@ class ResourceGroup(stack_resource.StackResource):
return [grouputils.get_rsrc_attr(self, key, False, n, *path)
for n in names]
def _build_resource_definition(self, include_all=False):
def build_resource_definition(self, res_name, res_defn):
res_def = copy.deepcopy(res_defn)
props = res_def.get(self.RESOURCE_DEF_PROPERTIES)
if props:
repl_props = self._handle_repl_val(res_name, props)
res_def[self.RESOURCE_DEF_PROPERTIES] = repl_props
return template.HOTemplate20130523.rsrc_defn_from_snippet(res_name,
res_def)
def get_resource_def(self, include_all=False):
res_def = self.properties[self.RESOURCE_DEF]
if res_def[self.RESOURCE_DEF_PROPERTIES] is None:
res_def[self.RESOURCE_DEF_PROPERTIES] = {}
if res_def[self.RESOURCE_DEF_METADATA] is None:
del res_def[self.RESOURCE_DEF_METADATA]
if not include_all:
resource_def_props = res_def[self.RESOURCE_DEF_PROPERTIES]
clean = dict((k, v) for k, v in resource_def_props.items()
if v is not None)
res_def[self.RESOURCE_DEF_PROPERTIES] = clean
return self._clean_props(res_def)
return res_def
def _clean_props(self, res_defn):
res_def = copy.deepcopy(res_defn)
props = res_def.get(self.RESOURCE_DEF_PROPERTIES)
if props:
clean = dict((k, v) for k, v in props.items() if v is not None)
props = clean
res_def[self.RESOURCE_DEF_PROPERTIES] = props
return res_def
def _handle_repl_val(self, res_name, val):
@ -470,29 +475,25 @@ class ResourceGroup(stack_resource.StackResource):
if isinstance(val, six.string_types):
return val.replace(repl_var, res_name)
elif isinstance(val, collections.Mapping):
return dict(zip(val, map(recurse, six.itervalues(val))))
return {k: recurse(v) for k, v in val.items()}
elif isinstance(val, collections.Sequence):
return map(recurse, val)
return [recurse(v) for v in val]
return val
def _do_prop_replace(self, res_name, res_def_template):
res_def = copy.deepcopy(res_def_template)
props = res_def[self.RESOURCE_DEF_PROPERTIES]
if props:
props = self._handle_repl_val(res_name, props)
res_def[self.RESOURCE_DEF_PROPERTIES] = props
return res_def
def _assemble_nested(self, names, include_all=False,
template_version=('heat_template_version',
'2015-04-30')):
def _assemble_nested(self, names, include_all=False):
res_def = self._build_resource_definition(include_all)
resources = dict((k, self._do_prop_replace(k, res_def))
for k in names)
child_template = copy.deepcopy(template_template)
child_template['resources'] = resources
return child_template
def_dict = self.get_resource_def(include_all)
definitions = [(k, self.build_resource_definition(k, def_dict))
for k in names]
return scl_template.make_template(definitions,
version=template_version)
def _assemble_for_rolling_update(self, total_capacity, max_updates,
include_all=False):
include_all=False,
template_version=('heat_template_version',
'2015-04-30')):
names = list(self._resource_names(total_capacity))
name_blacklist = self._name_blacklist()
@ -517,21 +518,18 @@ class ResourceGroup(stack_resource.StackResource):
return total_capacity
old_resources = sorted(valid_resources, key=replace_priority)
existing_names = set(n for n, d in valid_resources)
new_names = six.moves.filterfalse(lambda n: n in existing_names,
names)
res_def = self._build_resource_definition(include_all)
resources = scale_template.member_definitions(old_resources, res_def,
total_capacity,
max_updates,
lambda: next(new_names),
self._do_prop_replace)
child_template = copy.deepcopy(template_template)
child_template['resources'] = dict(resources)
return child_template
res_def = self.get_resource_def(include_all)
definitions = scl_template.member_definitions(
old_resources, res_def,
total_capacity,
max_updates,
lambda: next(new_names),
self.build_resource_definition)
return scl_template.make_template(definitions,
version=template_version)
def _try_rolling_update(self):
if self.update_policy[self.ROLLING_UPDATE]:

View File

@ -27,6 +27,7 @@ from heat.engine import resource
from heat.engine.resources.openstack.heat import resource_group
from heat.engine.resources.openstack.heat import software_config as sc
from heat.engine.resources import signal_responder
from heat.engine import rsrc_defn
from heat.engine import support
from heat.rpc import api as rpc_api
@ -620,25 +621,16 @@ class SoftwareDeploymentGroup(resource_group.ResourceGroup):
def _resource_names(self):
return six.iterkeys(self.properties.get(self.SERVERS, {}))
def _do_prop_replace(self, res_name, res_def_template):
res_def = copy.deepcopy(res_def_template)
props = res_def[self.RESOURCE_DEF_PROPERTIES]
servers = self.properties.get(self.SERVERS, {})
props[SoftwareDeployment.SERVER] = servers.get(res_name)
return res_def
def get_resource_def(self, include_all=False):
return dict(self.properties)
def _build_resource_definition(self, include_all=False):
p = self.properties
return {
self.RESOURCE_DEF_TYPE: 'OS::Heat::SoftwareDeployment',
self.RESOURCE_DEF_PROPERTIES: {
self.CONFIG: p[self.CONFIG],
self.INPUT_VALUES: p[self.INPUT_VALUES],
self.DEPLOY_ACTIONS: p[self.DEPLOY_ACTIONS],
self.SIGNAL_TRANSPORT: p[self.SIGNAL_TRANSPORT],
self.NAME: p[self.NAME],
}
}
def build_resource_definition(self, res_name, res_defn):
props = copy.deepcopy(res_defn)
servers = props.pop(self.SERVERS)
props[SoftwareDeployment.SERVER] = servers.get(res_name)
return rsrc_defn.ResourceDefinition(res_name,
'OS::Heat::SoftwareDeployment',
props, None)
def FnGetAtt(self, key, *path):
rg = super(SoftwareDeploymentGroup, self)

View File

@ -12,6 +12,7 @@
# under the License.
import collections
import copy
import functools
import six
@ -22,6 +23,7 @@ from heat.engine import constraints
from heat.engine import properties
from heat.engine.resources.openstack.heat import software_config as sc
from heat.engine.resources.openstack.heat import software_deployment as sd
from heat.engine import rsrc_defn
from heat.engine import support
@ -217,20 +219,13 @@ class StructuredDeploymentGroup(sd.SoftwareDeploymentGroup):
StructuredDeployment.properties_schema[INPUT_VALUES_VALIDATE],
}
def _build_resource_definition(self, include_all=False):
p = self.properties
return {
self.RESOURCE_DEF_TYPE: 'OS::Heat::StructuredDeployment',
self.RESOURCE_DEF_PROPERTIES: {
self.CONFIG: p[self.CONFIG],
self.INPUT_VALUES: p[self.INPUT_VALUES],
self.DEPLOY_ACTIONS: p[self.DEPLOY_ACTIONS],
self.SIGNAL_TRANSPORT: p[self.SIGNAL_TRANSPORT],
self.NAME: p[self.NAME],
self.INPUT_KEY: p[self.INPUT_KEY],
self.INPUT_VALUES_VALIDATE: p[self.INPUT_VALUES_VALIDATE],
}
}
def build_resource_definition(self, res_name, res_defn):
props = copy.deepcopy(res_defn)
servers = props.pop(self.SERVERS)
props[StructuredDeployment.SERVER] = servers.get(res_name)
return rsrc_defn.ResourceDefinition(res_name,
'OS::Heat::StructuredDeployment',
props, None)
class StructuredDeployments(StructuredDeploymentGroup):

View File

@ -283,7 +283,10 @@ class StackResource(resource.Resource):
if 'environment' not in adopt_data:
adopt_data['environment'] = child_env.user_env_as_dict()
if 'template' not in adopt_data:
adopt_data['template'] = child_template
if isinstance(child_template, template.Template):
adopt_data['template'] = child_template.t
else:
adopt_data['template'] = child_template
adopt_data_str = json.dumps(adopt_data)
args = {rpc_api.PARAM_TIMEOUT: timeout_mins,

View File

@ -26,6 +26,7 @@ from heat.engine import stack as stackm
from heat.tests import common
from heat.tests import utils
template = {
"heat_template_version": "2013-05-23",
"resources": {
@ -153,7 +154,7 @@ class ResourceGroupTest(common.HeatTestCase):
}
}
self.assertEqual(templ, resg._assemble_nested(['0', '1', '2']))
self.assertEqual(templ, resg._assemble_nested(['0', '1', '2']).t)
def test_assemble_nested_include(self):
templ = copy.deepcopy(template)
@ -171,12 +172,12 @@ class ResourceGroupTest(common.HeatTestCase):
}
}
}
self.assertEqual(expect, resg._assemble_nested(['0']))
self.assertEqual(expect, resg._assemble_nested(['0']).t)
expect['resources']["0"]['properties'] = {"Foo": None}
self.assertEqual(
expect, resg._assemble_nested(['0'], include_all=True))
expect, resg._assemble_nested(['0'], include_all=True).t)
def test_assemble_nested_zero(self):
def test_assemble_nested_include_zero(self):
templ = copy.deepcopy(template)
templ['resources']['group1']['properties']['count'] = 0
stack = utils.parse_stack(templ)
@ -184,9 +185,8 @@ class ResourceGroupTest(common.HeatTestCase):
resg = resource_group.ResourceGroup('test', snip, stack)
expect = {
"heat_template_version": "2015-04-30",
"resources": {}
}
self.assertEqual(expect, resg._assemble_nested([]))
self.assertEqual(expect, resg._assemble_nested([]).t)
def test_assemble_nested_with_metadata(self):
templ = copy.deepcopy(template)
@ -212,7 +212,7 @@ class ResourceGroupTest(common.HeatTestCase):
}
}
}
self.assertEqual(expect, resg._assemble_nested(['0']))
self.assertEqual(expect, resg._assemble_nested(['0']).t)
def test_assemble_nested_rolling_update(self):
expect = {
@ -232,18 +232,17 @@ class ResourceGroupTest(common.HeatTestCase):
}
}
}
resource_def = {
"type": "OverwrittenFnGetRefIdType",
"properties": {
"foo": "baz"
}
}
resource_def = rsrc_defn.ResourceDefinition(
None,
"OverwrittenFnGetRefIdType",
{"foo": "baz"})
stack = utils.parse_stack(template)
snip = stack.t.resource_definitions(stack)['group1']
resg = resource_group.ResourceGroup('test', snip, stack)
resg._nested = get_fake_nested_stack(['0', '1'])
resg._build_resource_definition = mock.Mock(return_value=resource_def)
self.assertEqual(expect, resg._assemble_for_rolling_update(2, 1))
resg.build_resource_definition = mock.Mock(return_value=resource_def)
self.assertEqual(expect, resg._assemble_for_rolling_update(2, 1).t)
def test_assemble_nested_rolling_update_none(self):
expect = {
@ -263,18 +262,18 @@ class ResourceGroupTest(common.HeatTestCase):
}
}
}
resource_def = {
"type": "OverwrittenFnGetRefIdType",
"properties": {
"foo": "baz"
}
}
resource_def = rsrc_defn.ResourceDefinition(
None,
"OverwrittenFnGetRefIdType",
{"foo": "baz"})
stack = utils.parse_stack(template)
snip = stack.t.resource_definitions(stack)['group1']
resg = resource_group.ResourceGroup('test', snip, stack)
resg._nested = get_fake_nested_stack(['0', '1'])
resg._build_resource_definition = mock.Mock(return_value=resource_def)
self.assertEqual(expect, resg._assemble_for_rolling_update(2, 0))
resg.build_resource_definition = mock.Mock(return_value=resource_def)
self.assertEqual(expect, resg._assemble_for_rolling_update(2, 0).t)
def test_assemble_nested_rolling_update_failed_resource(self):
expect = {
@ -294,20 +293,19 @@ class ResourceGroupTest(common.HeatTestCase):
}
}
}
resource_def = {
"type": "OverwrittenFnGetRefIdType",
"properties": {
"foo": "baz"
}
}
resource_def = rsrc_defn.ResourceDefinition(
None,
"OverwrittenFnGetRefIdType",
{"foo": "baz"})
stack = utils.parse_stack(template)
snip = stack.t.resource_definitions(stack)['group1']
resg = resource_group.ResourceGroup('test', snip, stack)
resg._nested = get_fake_nested_stack(['0', '1'])
res0 = resg._nested['0']
res0.status = res0.FAILED
resg._build_resource_definition = mock.Mock(return_value=resource_def)
self.assertEqual(expect, resg._assemble_for_rolling_update(2, 1))
resg.build_resource_definition = mock.Mock(return_value=resource_def)
self.assertEqual(expect, resg._assemble_for_rolling_update(2, 1).t)
def test_index_var(self):
stack = utils.parse_stack(template_repl)
@ -334,6 +332,7 @@ class ResourceGroupTest(common.HeatTestCase):
]
}
},
"2": {
"type": "ResourceWithListProp%index%",
"properties": {
@ -345,7 +344,7 @@ class ResourceGroupTest(common.HeatTestCase):
}
}
}
nested = resg._assemble_nested(['0', '1', '2'])
nested = resg._assemble_nested(['0', '1', '2']).t
for res in nested['resources']:
nested['resources'][res]['properties']['listprop'] = \
list(nested['resources'][res]['properties']['listprop'])
@ -371,7 +370,7 @@ class ResourceGroupTest(common.HeatTestCase):
}
}
}
nested = resg._assemble_nested(['0'])
nested = resg._assemble_nested(['0']).t
nested['resources']['0']['properties']['listprop'] = \
list(nested['resources']['0']['properties']['listprop'])
self.assertEqual(expect, nested)
@ -396,7 +395,7 @@ class ResourceGroupTest(common.HeatTestCase):
}
}
}
nested = resg._assemble_nested(['0'])
nested = resg._assemble_nested(['0']).t
nested['resources']['0']['properties']['listprop'] = \
list(nested['resources']['0']['properties']['listprop'])
self.assertEqual(expect, nested)
@ -600,7 +599,7 @@ class ResourceGroupBlackList(common.HeatTestCase):
class ResourceGroupEmptyParams(common.HeatTestCase):
"""This class tests ResourceGroup._build_resource_definition()."""
"""This class tests ResourceGroup.build_resource_definition()."""
scenarios = [
('non_empty', dict(value='Bar', expected={'Foo': 'Bar'},
@ -626,17 +625,21 @@ class ResourceGroupEmptyParams(common.HeatTestCase):
stack = utils.parse_stack(templ)
snip = stack.t.resource_definitions(stack)['group1']
resg = resource_group.ResourceGroup('test', snip, stack)
exp1 = {
"type": "OverwrittenFnGetRefIdType",
"properties": self.expected,
}
exp2 = {
"type": "OverwrittenFnGetRefIdType",
"properties": self.expected_include,
}
self.assertEqual(exp1, resg._build_resource_definition())
exp1 = rsrc_defn.ResourceDefinition(
None,
"OverwrittenFnGetRefIdType",
self.expected)
exp2 = rsrc_defn.ResourceDefinition(
None,
"OverwrittenFnGetRefIdType",
self.expected_include)
rdef = resg.get_resource_def()
self.assertEqual(exp1, resg.build_resource_definition('0', rdef))
rdef = resg.get_resource_def(include_all=True)
self.assertEqual(
exp2, resg._build_resource_definition(include_all=True))
exp2, resg.build_resource_definition('0', rdef))
class ResourceGroupNameListTest(common.HeatTestCase):
@ -1368,12 +1371,20 @@ class TestGetBatches(common.HeatTestCase):
self.assertEqual([(s, u) for s, u, n in self.batches], batches)
def test_assemble(self):
resources = [(str(i), False) for i in range(self.init_cap + 1)]
old_def = rsrc_defn.ResourceDefinition(
None,
"OverwrittenFnGetRefIdType",
{"foo": "baz"})
new_def = rsrc_defn.ResourceDefinition(
None,
"OverwrittenFnGetRefIdType",
{"foo": "bar"})
resources = [(str(i), old_def) for i in range(self.init_cap + 1)]
self.grp.get_size = mock.Mock(return_value=self.targ_cap)
self.grp._build_resource_definition = mock.Mock(return_value=True)
self.grp.build_resource_definition = mock.Mock(return_value=new_def)
self.grp._get_resources = mock.Mock(return_value=resources)
self.grp._do_prop_replace = mock.Mock(side_effect=lambda g, d: d)
all_updated_names = set()
@ -1382,14 +1393,14 @@ class TestGetBatches(common.HeatTestCase):
template = self.grp._assemble_for_rolling_update(size,
max_upd,
names)
res_dict = template['resources']
res_dict = template.resource_definitions(self.stack)
expected_names = set(map(str, range(1, size + 1)))
self.assertEqual(expected_names, set(res_dict))
all_updated_names &= expected_names
all_updated_names |= set(names)
updated = set(n for n, v in res_dict.items() if v is True)
updated = set(n for n, v in res_dict.items() if v != old_def)
self.assertEqual(all_updated_names, updated)
resources[:] = sorted(res_dict.items(), key=lambda i: int(i[0]))

View File

@ -1174,20 +1174,23 @@ class SoftwareDeploymentGroupTest(common.HeatTestCase):
stack = utils.parse_stack(self.template)
snip = stack.t.resource_definitions(stack)['deploy_mysql']
resg = sd.SoftwareDeploymentGroup('test', snip, stack)
expect = {
'type': 'OS::Heat::SoftwareDeployment',
'properties': {
'actions': ['CREATE', 'UPDATE'],
expect = rsrc_defn.ResourceDefinition(
None,
"OS::Heat::SoftwareDeployment",
{'actions': ['CREATE', 'UPDATE'],
'config': 'config_uuid',
'input_values': {'foo': 'bar'},
'name': '10_config',
'signal_transport': 'CFN_SIGNAL'
}
}
'server': 'uuid1',
'signal_transport': 'CFN_SIGNAL'})
rdef = resg.get_resource_def()
self.assertEqual(
expect, resg._build_resource_definition())
expect, resg.build_resource_definition('server1', rdef))
rdef = resg.get_resource_def(include_all=True)
self.assertEqual(
expect, resg._build_resource_definition(include_all=True))
expect, resg.build_resource_definition('server1', rdef))
def test_resource_names(self):
stack = utils.parse_stack(self.template)
@ -1240,7 +1243,8 @@ class SoftwareDeploymentGroupTest(common.HeatTestCase):
}
}
self.assertEqual(templ, resg._assemble_nested(['server1', 'server2']))
self.assertEqual(templ, resg._assemble_nested(['server1',
'server2']).t)
def test_attributes(self):
stack = utils.parse_stack(self.template)

View File

@ -15,6 +15,7 @@ import mock
from heat.common import exception
from heat.engine.resources.openstack.heat import structured_config as sc
from heat.engine import rsrc_defn
from heat.engine import stack as parser
from heat.engine import template
from heat.tests import common
@ -288,22 +289,24 @@ class StructuredDeploymentGroupTest(common.HeatTestCase):
stack = utils.parse_stack(self.template)
snip = stack.t.resource_definitions(stack)['deploy_mysql']
resg = sc.StructuredDeploymentGroup('test', snip, stack)
expect = {
'type': 'OS::Heat::StructuredDeployment',
'properties': {
'actions': ['CREATE', 'UPDATE'],
'config': 'config_uuid',
'input_key': 'get_input',
'input_values': None,
'name': None,
'signal_transport': 'CFN_SIGNAL',
'input_values_validate': 'LAX'
}
}
expect = rsrc_defn.ResourceDefinition(
None,
'OS::Heat::StructuredDeployment',
{'actions': ['CREATE', 'UPDATE'],
'config': 'config_uuid',
'input_values': None,
'name': None,
'server': 'uuid1',
'input_key': 'get_input',
'signal_transport': 'CFN_SIGNAL',
'input_values_validate': 'LAX'})
rdef = resg.get_resource_def()
self.assertEqual(
expect, resg._build_resource_definition())
expect, resg.build_resource_definition('server1', rdef))
rdef = resg.get_resource_def(include_all=True)
self.assertEqual(
expect, resg._build_resource_definition(include_all=True))
expect, resg.build_resource_definition('server1', rdef))
def test_resource_names(self):
stack = utils.parse_stack(self.template)
@ -360,7 +363,8 @@ class StructuredDeploymentGroupTest(common.HeatTestCase):
}
}
self.assertEqual(templ, resg._assemble_nested(['server1', 'server2']))
self.assertEqual(templ, resg._assemble_nested(['server1',
'server2']).t)
class StructuredDeploymentWithStrictInputParseTest(common.HeatTestCase):