]> review.fuel-infra Code Review - openstack-build/heat-build.git/commitdiff
Add the AutoScalingGroupName to the server Tags
authorAngus Salkeld <asalkeld@redhat.com>
Wed, 31 Jul 2013 11:28:46 +0000 (21:28 +1000)
committerAngus Salkeld <asalkeld@redhat.com>
Thu, 1 Aug 2013 10:49:34 +0000 (20:49 +1000)
Then use that to find the correct watch rule.

blueprint watch-ceilometer
Change-Id: I11f1556c1a55a06e1bf47e0baa156e0765f711ce

heat/api/cloudwatch/watch.py
heat/engine/resources/autoscaling.py
heat/engine/service.py
heat/engine/watchrule.py
heat/tests/test_server_tags.py
heat/tests/test_watch.py

index ca8dc13fde647e428640b23db5ba4a33d346c2f2..8082b70a1d9f3078f5386ac0fccc35f3a737b939 100644 (file)
@@ -283,15 +283,6 @@ class WatchController(object):
             else:
                 dimensions.append(dimension)
 
-        # We expect an AlarmName dimension as currently the engine
-        # implementation requires metric data to be associated
-        # with an alarm.  When this is fixed, we can simply
-        # parse the user-defined dimensions and add the list to
-        # the metric data
-        if not watch_name:
-            logger.error("Request does not contain AlarmName dimension!")
-            return exception.HeatMissingParameterError("AlarmName dimension")
-
         # Extract the required data from the metric_data
         # and format dict to pass to engine
         data = {'Namespace': namespace,
index f1ae64ca9b19b97f808802301e6897ce4048477a..5a8c48f07a8bd9c648d7007734b6c71adc91bbfd 100644 (file)
@@ -135,11 +135,22 @@ class InstanceGroup(resource.Resource):
         instance_definition = self.stack.t['Resources'][conf]
 
         # honour the Tags property in the InstanceGroup and AutoScalingGroup
-        tags = self.properties.data.get('Tags', [])
-        instance_definition['Properties']['Tags'] = tags
-
+        instance_definition['Properties']['Tags'] = self._tags()
         return GroupedInstance(name, instance_definition, self.stack)
 
+    def _tags(self):
+        """
+        Make sure that we add a tag that Ceilometer can pick up.
+        These need to be prepended with 'metering.'.
+        """
+        tags = self.properties.get('Tags') or []
+        for t in tags:
+            if t['Key'].startswith('metering.'):
+                # the user has added one, don't add another.
+                return tags
+        return tags + [{'Key': 'metering.groupname',
+                        'Value': self.FnGetRefId()}]
+
     def _instances(self):
         '''
         Convert the stored instance list into a list of GroupedInstance objects
@@ -323,7 +334,6 @@ class AutoScalingGroup(InstanceGroup, CooldownMixin):
                                  'Cooldown', 'DesiredCapacity',)
 
     def handle_create(self):
-
         if self.properties['DesiredCapacity']:
             num_to_create = int(self.properties['DesiredCapacity'])
         else:
@@ -406,6 +416,17 @@ class AutoScalingGroup(InstanceGroup, CooldownMixin):
 
         return result
 
+    def _tags(self):
+        """Add Identifing Tags to all servers in the group.
+
+        This is so the Dimensions received from cfn-push-stats all include
+        the groupname and stack id.
+        Note: the group name must match what is returned from FnGetRefId
+        """
+        autoscaling_tag = [{'Key': 'AutoScalingGroupName',
+                            'Value': self.FnGetRefId()}]
+        return super(AutoScalingGroup, self)._tags() + autoscaling_tag
+
     def FnGetRefId(self):
         return unicode(self.name)
 
index 0c438eef9ce887bb1391468527b2767295785d5b..cf36e6aa7c8802af1bfc8e91aade7acb1a15bc95 100644 (file)
@@ -662,9 +662,24 @@ class EngineService(service.Service):
         This could be used by CloudWatch and WaitConditions
         and treat HA service events like any other CloudWatch.
         '''
-        rule = watchrule.WatchRule.load(cnxt, watch_name)
-        rule.create_watch_data(stats_data)
-        logger.debug('new watch:%s data:%s' % (watch_name, str(stats_data)))
+        def get_matching_watches():
+            if watch_name:
+                yield watchrule.WatchRule.load(cnxt, watch_name)
+            else:
+                for wr in db_api.watch_rule_get_all(cnxt):
+                    if watchrule.rule_can_use_sample(wr, stats_data):
+                        yield watchrule.WatchRule.load(cnxt, watch=wr)
+
+        rule_run = False
+        for rule in get_matching_watches():
+            rule.create_watch_data(stats_data)
+            rule_run = True
+
+        if not rule_run:
+            if watch_name is None:
+                watch_name = 'Unknown'
+            raise exception.WatchRuleNotFound(watch_name=watch_name)
+
         return stats_data
 
     @request_context
index 7f6d30fd8b8886d37cfc56d47f6b1c471409eab4..3f2f82de6b68e294726f2948ee3c5fa497089372 100644 (file)
@@ -309,3 +309,32 @@ class WatchRule(object):
                 logger.warning("Unable to override state %s for watch %s" %
                               (self.state, self.name))
         return actions
+
+
+def rule_can_use_sample(wr, stats_data):
+    def match_dimesions(rule, data):
+        for k, v in iter(rule.items()):
+            if k not in data:
+                return False
+            elif v != data[k]:
+                return False
+        return True
+
+    if wr.state == WatchRule.SUSPENDED:
+        return False
+    if wr.rule['MetricName'] not in stats_data:
+        return False
+
+    rule_dims = dict((d['Name'], d['Value'])
+                     for d in wr.rule.get('Dimensions', []))
+
+    for k, v in iter(stats_data.items()):
+        if k == 'Namespace':
+            continue
+        if k == wr.rule['MetricName']:
+            data_dims = v.get('Dimensions', {})
+            if isinstance(data_dims, list):
+                data_dims = data_dims[0]
+            if match_dimesions(rule_dims, data_dims):
+                return True
+    return False
index ef59889a3cc592726a88f8e54ed1131afc0e9d5b..091d53f98ac32d58f69852ddf290c984c70a710a 100644 (file)
@@ -168,6 +168,7 @@ class ServerTagsTest(HeatTestCase):
     def test_group_tags(self):
         tags = [{'Key': 'Food', 'Value': 'yum'}]
         metadata = dict((tm['Key'], tm['Value']) for tm in tags)
+        metadata['metering.groupname'] = 'WebServer'
         group = self._setup_test_group(intags=tags, nova_tags=metadata)
         self.m.ReplayAll()
         scheduler.TaskRunner(group.create)()
index b3238f07756c6eef0cd8764176eaafd42d199b9d..fe1acad64b169819876e63f4162b1988ba4963c7 100644 (file)
@@ -644,6 +644,137 @@ class WatchRuleTest(HeatTestCase):
         dbwr = db_api.watch_rule_get_by_name(self.ctx, 'create_data_test')
         self.assertEqual(dbwr.watch_data, [])
 
+    @utils.wr_delete_after
+    def test_create_watch_data_match(self):
+        rule = {u'EvaluationPeriods': u'1',
+                u'AlarmDescription': u'test alarm',
+                u'Period': u'300',
+                u'ComparisonOperator': u'GreaterThanThreshold',
+                u'Statistic': u'SampleCount',
+                u'Threshold': u'2',
+                u'Dimensions': [{u'Name': 'AutoScalingGroupName',
+                                 u'Value': 'group_x'}],
+                u'MetricName': u'CreateDataMetric'}
+        self.wr = watchrule.WatchRule(context=self.ctx,
+                                      watch_name='create_data_test',
+                                      stack_id=self.stack_id, rule=rule)
+        self.wr.store()
+
+        data = {u'CreateDataMetric': {"Unit": "Counter",
+                                      "Value": "1",
+                                      "Dimensions": [{u'AutoScalingGroupName':
+                                                      u'group_x'}]}}
+        self.assertTrue(watchrule.rule_can_use_sample(self.wr, data))
+
+    @utils.wr_delete_after
+    def test_create_watch_data_match_2(self):
+        rule = {u'EvaluationPeriods': u'1',
+                u'AlarmDescription': u'test alarm',
+                u'Period': u'300',
+                u'ComparisonOperator': u'GreaterThanThreshold',
+                u'Statistic': u'SampleCount',
+                u'Threshold': u'2',
+                u'Dimensions': [{u'Name': 'AutoScalingGroupName',
+                                 u'Value': 'group_x'}],
+                u'MetricName': u'CreateDataMetric'}
+        self.wr = watchrule.WatchRule(context=self.ctx,
+                                      watch_name='create_data_test',
+                                      stack_id=self.stack_id, rule=rule)
+        self.wr.store()
+
+        data = {u'not_interesting': {"Unit": "Counter",
+                                     "Value": "1",
+                                     "Dimensions": [
+                                         {u'AutoScalingGroupName':
+                                          u'group_x'}]},
+                u'CreateDataMetric': {"Unit": "Counter",
+                                      "Value": "1",
+                                      "Dimensions": [
+                                          {u'AutoScalingGroupName':
+                                           u'group_x'}]}}
+        self.assertTrue(watchrule.rule_can_use_sample(self.wr, data))
+
+    def test_create_watch_data_match_3(self):
+        rule = {u'EvaluationPeriods': u'1',
+                u'AlarmDescription': u'test alarm',
+                u'Period': u'300',
+                u'ComparisonOperator': u'GreaterThanThreshold',
+                u'Statistic': u'SampleCount',
+                u'Threshold': u'2',
+                u'Dimensions': [{u'Name': 'AutoScalingGroupName',
+                                 u'Value': 'group_x'}],
+                u'MetricName': u'CreateDataMetric'}
+        self.wr = watchrule.WatchRule(context=self.ctx,
+                                      watch_name='create_data_test',
+                                      stack_id=self.stack_id, rule=rule)
+        self.wr.store()
+
+        data = {u'CreateDataMetric': {"Unit": "Counter",
+                                      "Value": "1",
+                                      "Dimensions": [
+                                          {u'AutoScalingGroupName':
+                                           u'not_this'}]},
+                u'CreateDataMetric': {"Unit": "Counter",
+                                      "Value": "1",
+                                      "Dimensions": [
+                                          {u'AutoScalingGroupName':
+                                           u'group_x'}]}}
+        self.assertTrue(watchrule.rule_can_use_sample(self.wr, data))
+
+    def test_create_watch_data_not_match_metric(self):
+        rule = {u'EvaluationPeriods': u'1',
+                u'AlarmDescription': u'test alarm',
+                u'Period': u'300',
+                u'ComparisonOperator': u'GreaterThanThreshold',
+                u'Statistic': u'SampleCount',
+                u'Threshold': u'2',
+                u'Dimensions': [{u'Name': 'AutoScalingGroupName',
+                                 u'Value': 'group_x'}],
+                u'MetricName': u'CreateDataMetric'}
+        self.wr = watchrule.WatchRule(context=self.ctx,
+                                      watch_name='create_data_test',
+                                      stack_id=self.stack_id, rule=rule)
+        self.wr.store()
+
+        data = {u'not_this': {"Unit": "Counter",
+                              "Value": "1",
+                              "Dimensions": [
+                                  {u'AutoScalingGroupName':
+                                   u'group_x'}]},
+                u'nor_this': {"Unit": "Counter",
+                              "Value": "1",
+                              "Dimensions": [
+                                  {u'AutoScalingGroupName':
+                                   u'group_x'}]}}
+        self.assertFalse(watchrule.rule_can_use_sample(self.wr, data))
+
+    def test_create_watch_data_not_match_dimensions(self):
+        rule = {u'EvaluationPeriods': u'1',
+                u'AlarmDescription': u'test alarm',
+                u'Period': u'300',
+                u'ComparisonOperator': u'GreaterThanThreshold',
+                u'Statistic': u'SampleCount',
+                u'Threshold': u'2',
+                u'Dimensions': [{u'Name': 'AutoScalingGroupName',
+                                 u'Value': 'group_x'}],
+                u'MetricName': u'CreateDataMetric'}
+        self.wr = watchrule.WatchRule(context=self.ctx,
+                                      watch_name='create_data_test',
+                                      stack_id=self.stack_id, rule=rule)
+        self.wr.store()
+
+        data = {u'CreateDataMetric': {"Unit": "Counter",
+                                      "Value": "1",
+                                      "Dimensions": [
+                                          {u'AutoScalingGroupName':
+                                           u'not_this'}]},
+                u'CreateDataMetric': {"Unit": "Counter",
+                                      "Value": "1",
+                                      "Dimensions": [
+                                          {u'wrong_key':
+                                           u'group_x'}]}}
+        self.assertFalse(watchrule.rule_can_use_sample(self.wr, data))
+
     def test_destroy(self):
         rule = {'EvaluationPeriods': '1',
                 'MetricName': 'test_metric',