]> review.fuel-infra Code Review - openstack-build/heat-build.git/commitdiff
Allow non-replacement updates of Alarms
authorAngus Salkeld <asalkeld@redhat.com>
Fri, 3 May 2013 04:25:57 +0000 (14:25 +1000)
committerAngus Salkeld <asalkeld@redhat.com>
Fri, 3 May 2013 04:25:57 +0000 (14:25 +1000)
blueprint cloudwatch-update-stack

Change-Id: I58982caf33bb5b326797472ec6c3dab8eba3a668

heat/engine/resources/cloud_watch.py
heat/tests/test_autoscaling.py

index e4d6f7254a34019cb452b3f311968cae65062aa7..9511094323149d85229d108877120419919fe564 100644 (file)
@@ -16,6 +16,7 @@
 from heat.common import exception
 from heat.engine import watchrule
 from heat.engine import resource
+from heat.engine.properties import Properties
 from heat.db import api as db_api
 
 from heat.openstack.common import log as logging
@@ -73,6 +74,14 @@ class CloudWatchAlarm(resource.Resource):
                                                      'Count/Second', None]}}
 
     strict_dependency = False
+    update_allowed_keys = ('Properties',)
+    # allow the properties that affect the watch calculation.
+    # note: when using in-instance monitoring you can only change the
+    # metric name if you re-configure the instance too.
+    update_allowed_properties = ('ComparisonOperator', 'AlarmDescription',
+                                 'EvaluationPeriods', 'Period', 'Statistic',
+                                 'AlarmActions', 'OKActions', 'Units'
+                                 'InsufficientDataActions', 'Threshold')
 
     def handle_create(self):
         wr = watchrule.WatchRule(context=self.context,
@@ -82,7 +91,33 @@ class CloudWatchAlarm(resource.Resource):
         wr.store()
 
     def handle_update(self, json_snippet):
-        return self.UPDATE_REPLACE
+        try:
+            self.update_template_diff(json_snippet)
+        except NotImplementedError:
+            logger.error("Could not update %s, invalid key" % self.name)
+            return self.UPDATE_REPLACE
+
+        try:
+            prop_diff = self.update_template_diff_properties(json_snippet)
+        except NotImplementedError:
+            logger.error("Could not update %s, invalid Property" % self.name)
+            return self.UPDATE_REPLACE
+
+        # If Properties has changed, update self.properties, so we
+        # get the new values during any subsequent adjustment
+        if prop_diff:
+            self.properties = Properties(self.properties_schema,
+                                         json_snippet.get('Properties', {}),
+                                         self.stack.resolve_runtime_data,
+                                         self.name)
+            loader = watchrule.WatchRule.load
+            wr = loader(self.context,
+                        watch_name=self.physical_resource_name())
+
+            wr.rule = self.parsed_template('Properties')
+            wr.store()
+
+        return self.UPDATE_COMPLETE
 
     def handle_delete(self):
         try:
index 8a88ede531eb8d02e3fd952c7eb8b2db7e3a16ca..30c9a4fd1d66390e207d6ba99c9e1ec6fbf17d43 100644 (file)
@@ -25,6 +25,7 @@ from heat.common import template_format
 from heat.engine.resources import autoscaling as asc
 from heat.engine.resources import loadbalancer
 from heat.engine.resources import instance
+from heat.engine.resources import cloud_watch
 from heat.engine import clients
 from heat.engine import parser
 from heat.engine import scheduler
@@ -84,6 +85,16 @@ class AutoScalingTest(HeatTestCase):
                          resource.state)
         return resource
 
+    def create_alarm(self, t, stack, resource_name):
+        resource = cloud_watch.CloudWatchAlarm(resource_name,
+                                               t['Resources'][resource_name],
+                                               stack)
+        self.assertEqual(None, resource.validate())
+        scheduler.TaskRunner(resource.create)()
+        self.assertEqual(cloud_watch.CloudWatchAlarm.CREATE_COMPLETE,
+                         resource.state)
+        return resource
+
     def _stub_create(self, num):
         self.m.StubOutWithMock(eventlet, 'sleep')
 
@@ -266,7 +277,6 @@ class AutoScalingTest(HeatTestCase):
         self.assertEqual('WebServerGroup', resource.FnGetRefId())
         self.assertEqual('WebServerGroup-0', resource.resource_id)
         update_snippet = copy.deepcopy(resource.parsed_template())
-        old_cd = update_snippet['Properties']['Cooldown']
         update_snippet['Properties']['Cooldown'] = '61'
         self.assertEqual(asc.AutoScalingGroup.UPDATE_COMPLETE,
                          resource.handle_update(update_snippet))
@@ -274,6 +284,65 @@ class AutoScalingTest(HeatTestCase):
         resource.delete()
         self.m.VerifyAll()
 
+    def test_mem_alarm_high_update_no_replace(self):
+        '''
+        Make sure that we can change the update-able properties
+        without replacing the Alarm resource.
+        '''
+        t = self.load_template()
+
+        #short circuit the alarm's references
+        properties = t['Resources']['MEMAlarmHigh']['Properties']
+        properties['AlarmActions'] = ['a']
+        properties['Dimensions'] = [{'a': 'v'}]
+
+        stack = self.parse_stack(t)
+        # the watch rule needs a valid stack_id
+        stack.store()
+
+        self.m.ReplayAll()
+        resource = self.create_alarm(t, stack, 'MEMAlarmHigh')
+        snippet = copy.deepcopy(resource.parsed_template())
+        snippet['Properties']['ComparisonOperator'] = 'LessThanThreshold'
+        snippet['Properties']['AlarmDescription'] = 'fruity'
+        snippet['Properties']['EvaluationPeriods'] = '2'
+        snippet['Properties']['Period'] = '90'
+        snippet['Properties']['Statistic'] = 'Maximum'
+        snippet['Properties']['Threshold'] = '39'
+
+        self.assertEqual(cloud_watch.CloudWatchAlarm.UPDATE_COMPLETE,
+                         resource.handle_update(snippet))
+
+        resource.delete()
+        self.m.VerifyAll()
+
+    def test_mem_alarm_high_update_replace(self):
+        '''
+        Make sure that the Alarm resource IS replaced when non-update-able
+        properties are changed.
+        '''
+        t = self.load_template()
+
+        #short circuit the alarm's references
+        properties = t['Resources']['MEMAlarmHigh']['Properties']
+        properties['AlarmActions'] = ['a']
+        properties['Dimensions'] = [{'a': 'v'}]
+
+        stack = self.parse_stack(t)
+        # the watch rule needs a valid stack_id
+        stack.store()
+
+        self.m.ReplayAll()
+        resource = self.create_alarm(t, stack, 'MEMAlarmHigh')
+        snippet = copy.deepcopy(resource.parsed_template())
+        snippet['Properties']['MetricName'] = 'temp'
+
+        self.assertEqual(cloud_watch.CloudWatchAlarm.UPDATE_REPLACE,
+                         resource.handle_update(snippet))
+
+        resource.delete()
+        self.m.VerifyAll()
+
     def test_scaling_group_adjust(self):
         t = self.load_template()
         stack = self.parse_stack(t)