Add the AutoScalingGroupName to the server Tags

Then use that to find the correct watch rule.

blueprint watch-ceilometer
Change-Id: I11f1556c1a55a06e1bf47e0baa156e0765f711ce
This commit is contained in:
Angus Salkeld 2013-07-31 21:28:46 +10:00
parent e6ec146fcf
commit 31e53d4fe7
6 changed files with 204 additions and 16 deletions

View 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,

View 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)

View 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

View 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

View 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)()

View 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',