From d32370233eaf2a5c32888f269bd1dc5e0e787467 Mon Sep 17 00:00:00 2001 From: Angus Salkeld Date: Fri, 10 Oct 2014 12:00:37 +1000 Subject: [PATCH] Make sure that the properties are stored on updates Previously properties_data was only stored on creation, now this is getting passed to the update mechanism too. Later we can look at reworking this into a single mechanism. Closes-bug: #1377681 Change-Id: If3d476f34b9e61a3c99f63ba33734a875353c8fc --- heat/engine/resource.py | 14 +++--- heat/tests/generic_resource.py | 4 +- heat/tests/test_neutron_autoscaling.py | 9 ++++ heat/tests/test_parser.py | 65 ++++++++++++++++++++++++++ heat/tests/test_properties.py | 12 +++++ 5 files changed, 97 insertions(+), 7 deletions(-) diff --git a/heat/engine/resource.py b/heat/engine/resource.py index b08222cc3a1..ccf0e6996a4 100644 --- a/heat/engine/resource.py +++ b/heat/engine/resource.py @@ -903,12 +903,14 @@ def _store_or_update(self, action, status, reason): 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 9843b14f6fe..de3665041db 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 4a266c4d635..6105287ed2c 100644 --- a/heat/tests/test_neutron_autoscaling.py +++ b/heat/tests/test_neutron_autoscaling.py @@ -122,6 +122,7 @@ def setUp(self): 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 @@ def __init__(self, id, name): 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 @@ def __init__(self, id, name): 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 1c84a82069f..4db1ce58537 100644 --- a/heat/tests/test_parser.py +++ b/heat/tests/test_parser.py @@ -924,6 +924,8 @@ def setUp(self): 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 @@ def test_update_modify_ok_replace(self): 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 3b310e3a2bb..616625a3b59 100644 --- a/heat/tests/test_properties.py +++ b/heat/tests/test_properties.py @@ -1485,6 +1485,18 @@ def test_schema_from_hot_params(self): 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):