diff --git a/heat/engine/resource.py b/heat/engine/resource.py index b08222cc3..ccf0e6996 100644 --- a/heat/engine/resource.py +++ b/heat/engine/resource.py @@ -903,12 +903,14 @@ class Resource(object): if self.id is not None: try: rs = db_api.resource_get(self.context, self.id) - rs.update_and_save({'action': self.action, - 'status': self.status, - 'status_reason': reason, - 'stack_id': self.stack.id, - 'updated_at': self.updated_time, - 'nova_instance': self.resource_id}) + rs.update_and_save({ + 'action': self.action, + 'status': self.status, + 'status_reason': reason, + 'stack_id': self.stack.id, + 'updated_at': self.updated_time, + 'properties_data': self._stored_properties_data, + 'nova_instance': self.resource_id}) except Exception as ex: LOG.error(_('DB error %s') % ex) diff --git a/heat/tests/generic_resource.py b/heat/tests/generic_resource.py index 9843b14f6..de3665041 100644 --- a/heat/tests/generic_resource.py +++ b/heat/tests/generic_resource.py @@ -60,11 +60,13 @@ class ResWithComplexPropsAndAttrs(GenericResource): properties_schema = {'a_string': {'Type': 'String'}, 'a_list': {'Type': 'List'}, - 'a_map': {'Type': 'Map'}} + 'a_map': {'Type': 'Map'}, + 'an_int': {'Type': 'Integer'}} attributes_schema = {'list': attributes.Schema('A list'), 'map': attributes.Schema('A map'), 'string': attributes.Schema('A string')} + update_allowed_properties = ('an_int',) def _resolve_attribute(self, name): try: diff --git a/heat/tests/test_neutron_autoscaling.py b/heat/tests/test_neutron_autoscaling.py index 4a266c4d6..6105287ed 100644 --- a/heat/tests/test_neutron_autoscaling.py +++ b/heat/tests/test_neutron_autoscaling.py @@ -122,6 +122,7 @@ class AutoScalingTest(HeatTestCase): self.m.StubOutWithMock(neutronclient.Client, 'show_pool') self.m.StubOutWithMock(neutronclient.Client, 'show_vip') self.m.StubOutWithMock(neutronclient.Client, 'create_member') + self.m.StubOutWithMock(neutronclient.Client, 'delete_member') self.m.StubOutWithMock(neutronclient.Client, 'list_members') self.m.StubOutWithMock(nova.NovaClientPlugin, 'server_to_ipaddress') @@ -309,6 +310,8 @@ class AutoScalingTest(HeatTestCase): nova.NovaClientPlugin.server_to_ipaddress( mox.IgnoreArg()).AndReturn('1.2.3.5') + neutronclient.Client.delete_member(mox.IgnoreArg()).AndReturn(None) + neutronclient.Client.create_member(memberb_block).\ AndReturn(memberb_ret_block) @@ -318,6 +321,12 @@ class AutoScalingTest(HeatTestCase): neutronclient.Client.create_member(memberc_block).\ AndReturn(memberc_ret_block) + nova.NovaClientPlugin.server_to_ipaddress( + mox.IgnoreArg()).AndReturn('1.2.3.4') + + neutronclient.Client.create_member(membera_block).\ + AndReturn(membera_ret_block) + self.m.ReplayAll() # Start of stack create diff --git a/heat/tests/test_parser.py b/heat/tests/test_parser.py index 1c84a8206..4db1ce585 100644 --- a/heat/tests/test_parser.py +++ b/heat/tests/test_parser.py @@ -924,6 +924,8 @@ class StackTest(HeatTestCase): generic_rsrc.ResourceWithProps) resource._register_class('ResourceWithComplexAttributesType', generic_rsrc.ResourceWithComplexAttributes) + resource._register_class('ResWithComplexPropsAndAttrs', + generic_rsrc.ResWithComplexPropsAndAttrs) def test_stack_reads_tenant(self): stack = parser.Stack(self.ctx, 'test_stack', self.tmpl, @@ -2180,6 +2182,69 @@ class StackTest(HeatTestCase): self.m.VerifyAll() + def test_update_modify_ok_replace_int(self): + # create + #======== + tmpl = {'heat_template_version': '2013-05-23', + 'resources': {'AResource': { + 'type': 'ResWithComplexPropsAndAttrs', + 'properties': {'an_int': 1}}}} + + self.stack = parser.Stack(self.ctx, 'update_test_stack', + template.Template(tmpl)) + self.stack.store() + stack_id = self.stack.id + self.stack.create() + self.assertEqual((parser.Stack.CREATE, parser.Stack.COMPLETE), + self.stack.state) + + value1 = 2 + prop_diff1 = {'an_int': value1} + value2 = 1 + prop_diff2 = {'an_int': value2} + + self.m.StubOutWithMock(generic_rsrc.ResWithComplexPropsAndAttrs, + 'handle_update') + generic_rsrc.ResWithComplexPropsAndAttrs.handle_update( + IgnoreArg(), IgnoreArg(), prop_diff1) + generic_rsrc.ResWithComplexPropsAndAttrs.handle_update( + IgnoreArg(), IgnoreArg(), prop_diff2) + + self.m.ReplayAll() + + # update 1 + #========== + + self.stack = parser.Stack.load(self.ctx, stack_id=stack_id) + tmpl2 = {'heat_template_version': '2013-05-23', + 'resources': {'AResource': { + 'type': 'ResWithComplexPropsAndAttrs', + 'properties': {'an_int': value1}}}} + updated_stack = parser.Stack(self.ctx, 'updated_stack', + template.Template(tmpl2)) + + self.stack.update(updated_stack) + self.assertEqual((parser.Stack.UPDATE, parser.Stack.COMPLETE), + self.stack.state) + + # update 2 + #========== + # reload the previous stack + self.stack = parser.Stack.load(self.ctx, stack_id=stack_id) + tmpl3 = {'heat_template_version': '2013-05-23', + 'resources': {'AResource': { + 'type': 'ResWithComplexPropsAndAttrs', + 'properties': {'an_int': value2}}}} + + updated_stack = parser.Stack(self.ctx, 'updated_stack', + template.Template(tmpl3)) + + self.stack.update(updated_stack) + self.assertEqual((parser.Stack.UPDATE, parser.Stack.COMPLETE), + self.stack.state) + + self.m.VerifyAll() + def test_update_modify_param_ok_replace(self): tmpl = { 'HeatTemplateFormatVersion': '2012-12-12', diff --git a/heat/tests/test_properties.py b/heat/tests/test_properties.py index 3b310e3a2..616625a3b 100644 --- a/heat/tests/test_properties.py +++ b/heat/tests/test_properties.py @@ -1485,6 +1485,18 @@ class PropertiesTest(testtools.TestCase): self.assertEqual(expected, dict((n, dict(s)) for n, s in props_schemata.items())) + def test_compare_same(self): + schema = {'foo': {'Type': 'Integer'}} + props_a = properties.Properties(schema, {'foo': 1}) + props_b = properties.Properties(schema, {'foo': 1}) + self.assertFalse(props_a != props_b) + + def test_compare_different(self): + schema = {'foo': {'Type': 'Integer'}} + props_a = properties.Properties(schema, {'foo': 0}) + props_b = properties.Properties(schema, {'foo': 1}) + self.assertTrue(props_a != props_b) + class PropertiesValidationTest(testtools.TestCase): def test_required(self):