Merge "alarm: Use new gnocchi aggregation API"
This commit is contained in:
commit
06c4740636
@ -77,28 +77,35 @@ class GnocchiThresholdEvaluator(evaluator.Evaluator):
|
|||||||
|
|
||||||
def _statistics(self, alarm, start, end):
|
def _statistics(self, alarm, start, end):
|
||||||
"""Retrieve statistics over the current window."""
|
"""Retrieve statistics over the current window."""
|
||||||
if alarm.type == 'gnocchi_metrics_threshold':
|
method = 'get'
|
||||||
url = ("%s/v1/metric_aggregation/?"
|
req = {
|
||||||
"aggregation=%s&start=%s&end=%s&%s") % (
|
'url': self.gnocchi_url + "/v1",
|
||||||
self.gnocchi_url,
|
'headers': self._get_headers(),
|
||||||
alarm.rule['aggregation_method'],
|
'params': {
|
||||||
start, end,
|
'aggregation': alarm.rule['aggregation_method'],
|
||||||
"&".join("metric=%s" % m
|
'start': start,
|
||||||
for m in alarm.rule['metrics']))
|
'end': end,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if alarm.type == 'gnocchi_aggregation_by_resources_threshold':
|
||||||
|
method = 'post'
|
||||||
|
req['url'] += "/aggregation/resource/%s/metric/%s" % (
|
||||||
|
alarm.rule['resource_type'], alarm.rule['metric'])
|
||||||
|
req['data'] = alarm.rule['query']
|
||||||
|
|
||||||
|
elif alarm.type == 'gnocchi_aggregation_by_metrics_threshold':
|
||||||
|
req['url'] += "/aggregation/metric"
|
||||||
|
req['params']['metric[]'] = alarm.rule['metrics']
|
||||||
|
|
||||||
elif alarm.type == 'gnocchi_resources_threshold':
|
elif alarm.type == 'gnocchi_resources_threshold':
|
||||||
url = ("%s/v1/resource/%s/%s/metric/%s/measures?"
|
req['url'] += "/resource/%s/%s/metric/%s/measures" % (
|
||||||
"aggregation=%s&start=%s&end=%s") % (
|
alarm.rule['resource_type'],
|
||||||
self.gnocchi_url,
|
alarm.rule['resource_id'], alarm.rule['metric'])
|
||||||
alarm.rule['resource_type'],
|
|
||||||
alarm.rule['resource_constraint'],
|
|
||||||
alarm.rule['metric'],
|
|
||||||
alarm.rule['aggregation_method'],
|
|
||||||
start, end)
|
|
||||||
|
|
||||||
LOG.debug(_('stats query %s') % url)
|
LOG.debug(_('stats query %s') % req['url'])
|
||||||
try:
|
try:
|
||||||
r = requests.get(url, headers=self._get_headers())
|
r = getattr(requests, method)(**req)
|
||||||
except Exception:
|
except Exception:
|
||||||
LOG.exception(_('alarm stats retrieval failed'))
|
LOG.exception(_('alarm stats retrieval failed'))
|
||||||
return []
|
return []
|
||||||
|
@ -16,7 +16,6 @@
|
|||||||
from oslo_config import cfg
|
from oslo_config import cfg
|
||||||
from oslo_serialization import jsonutils
|
from oslo_serialization import jsonutils
|
||||||
import requests
|
import requests
|
||||||
import uuid
|
|
||||||
import wsme
|
import wsme
|
||||||
from wsme import types as wtypes
|
from wsme import types as wtypes
|
||||||
|
|
||||||
@ -80,12 +79,12 @@ class AlarmGnocchiThresholdRule(base.AlarmRule):
|
|||||||
return jsonutils.loads(r.text).get('aggregation_methods', [])
|
return jsonutils.loads(r.text).get('aggregation_methods', [])
|
||||||
|
|
||||||
|
|
||||||
class AlarmGnocchiMetricOfResourcesThresholdRule(AlarmGnocchiThresholdRule):
|
class MetricOfResourceRule(AlarmGnocchiThresholdRule):
|
||||||
metric = wsme.wsattr(wtypes.text, mandatory=True)
|
metric = wsme.wsattr(wtypes.text, mandatory=True)
|
||||||
"The name of the metric"
|
"The name of the metric"
|
||||||
|
|
||||||
resource_constraint = wsme.wsattr(wtypes.text, mandatory=True)
|
resource_id = wsme.wsattr(wtypes.text, mandatory=True)
|
||||||
"The id of a resource or a expression to select multiple resources"
|
"The id of a resource"
|
||||||
|
|
||||||
resource_type = wsme.wsattr(wtypes.text, mandatory=True)
|
resource_type = wsme.wsattr(wtypes.text, mandatory=True)
|
||||||
"The resource type"
|
"The resource type"
|
||||||
@ -95,46 +94,95 @@ class AlarmGnocchiMetricOfResourcesThresholdRule(AlarmGnocchiThresholdRule):
|
|||||||
'threshold', 'aggregation_method',
|
'threshold', 'aggregation_method',
|
||||||
'evaluation_periods',
|
'evaluation_periods',
|
||||||
'metric',
|
'metric',
|
||||||
'resource_constraint',
|
'resource_id',
|
||||||
'resource_type'])
|
'resource_type'])
|
||||||
return rule
|
return rule
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def validate_alarm(cls, alarm):
|
def validate_alarm(cls, alarm):
|
||||||
super(AlarmGnocchiMetricOfResourcesThresholdRule,
|
super(MetricOfResourceRule,
|
||||||
cls).validate_alarm(alarm)
|
cls).validate_alarm(alarm)
|
||||||
|
|
||||||
rule = alarm.gnocchi_resources_threshold_rule
|
rule = alarm.gnocchi_resources_threshold_rule
|
||||||
|
ks_client = keystone_client.get_client()
|
||||||
|
gnocchi_url = cfg.CONF.alarms.gnocchi_url
|
||||||
|
headers = {'Content-Type': "application/json",
|
||||||
|
'X-Auth-Token': ks_client.auth_token}
|
||||||
try:
|
try:
|
||||||
uuid.UUID(rule.resource_constraint)
|
r = requests.get("%s/v1/resource/%s/%s" % (
|
||||||
except Exception:
|
gnocchi_url, rule.resource_type,
|
||||||
auth_project = v2_utils.get_auth_project(alarm.project_id)
|
rule.resource_id),
|
||||||
if auth_project:
|
headers=headers)
|
||||||
# NOTE(sileht): when we have more complex query allowed
|
except requests.ConnectionError as e:
|
||||||
# this should be enhanced to ensure the constraint are still
|
raise GnocchiUnavailable(e)
|
||||||
# scoped to auth_project
|
if r.status_code == 404:
|
||||||
rule.resource_constraint += (
|
raise base.EntityNotFound('gnocchi resource',
|
||||||
u'\u2227project_id=%s' % auth_project)
|
rule.resource_id)
|
||||||
else:
|
elif r.status_code // 200 != 1:
|
||||||
ks_client = keystone_client.get_client()
|
raise base.ClientSideError(r.content, status_code=r.status_code)
|
||||||
gnocchi_url = cfg.CONF.alarms.gnocchi_url
|
|
||||||
headers = {'Content-Type': "application/json",
|
|
||||||
'X-Auth-Token': ks_client.auth_token}
|
|
||||||
try:
|
|
||||||
r = requests.get("%s/v1/resource/%s/%s" % (
|
|
||||||
gnocchi_url, rule.resource_type,
|
|
||||||
rule.resource_constraint),
|
|
||||||
headers=headers)
|
|
||||||
except requests.ConnectionError as e:
|
|
||||||
raise GnocchiUnavailable(e)
|
|
||||||
if r.status_code == 404:
|
|
||||||
raise base.EntityNotFound('gnocchi resource',
|
|
||||||
rule.resource_constraint)
|
|
||||||
elif r.status_code // 200 != 1:
|
|
||||||
raise base.ClientSideError(r.body, status_code=r.status_code)
|
|
||||||
|
|
||||||
|
|
||||||
class AlarmGnocchiMetricsThresholdRule(AlarmGnocchiThresholdRule):
|
class AggregationMetricByResourcesLookupRule(AlarmGnocchiThresholdRule):
|
||||||
|
metric = wsme.wsattr(wtypes.text, mandatory=True)
|
||||||
|
"The name of the metric"
|
||||||
|
|
||||||
|
query = wsme.wsattr(wtypes.text, mandatory=True)
|
||||||
|
"The query to filter the metric"
|
||||||
|
|
||||||
|
resource_type = wsme.wsattr(wtypes.text, mandatory=True)
|
||||||
|
"The resource type"
|
||||||
|
|
||||||
|
def as_dict(self):
|
||||||
|
rule = self.as_dict_from_keys(['granularity', 'comparison_operator',
|
||||||
|
'threshold', 'aggregation_method',
|
||||||
|
'evaluation_periods',
|
||||||
|
'metric',
|
||||||
|
'query',
|
||||||
|
'resource_type'])
|
||||||
|
return rule
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def validate_alarm(cls, alarm):
|
||||||
|
super(AggregationMetricByResourcesLookupRule,
|
||||||
|
cls).validate_alarm(alarm)
|
||||||
|
|
||||||
|
rule = alarm.gnocchi_aggregation_by_resources_threshold_rule
|
||||||
|
|
||||||
|
# check the query string is a valid json
|
||||||
|
try:
|
||||||
|
query = jsonutils.loads(rule.query)
|
||||||
|
except ValueError:
|
||||||
|
raise wsme.exc.InvalidInput('rule/query', rule.query)
|
||||||
|
|
||||||
|
# Scope the alarm to the project id if needed
|
||||||
|
auth_project = v2_utils.get_auth_project(alarm.project_id)
|
||||||
|
if auth_project:
|
||||||
|
rule.query = jsonutils.dumps({
|
||||||
|
"and": [{"=": {"created_by_project_id": auth_project}},
|
||||||
|
query]})
|
||||||
|
|
||||||
|
# Delegate the query validation to gnocchi
|
||||||
|
ks_client = keystone_client.get_client()
|
||||||
|
request = {
|
||||||
|
'url': "%s/v1/aggregation/resource/%s/metric/%s/measures" % (
|
||||||
|
cfg.CONF.alarms.gnocchi_url,
|
||||||
|
rule.resource_type,
|
||||||
|
rule.metric),
|
||||||
|
'headers': {'Content-Type': "application/json",
|
||||||
|
'X-Auth-Token': ks_client.auth_token},
|
||||||
|
'params': {'aggregation': rule.aggregation_method},
|
||||||
|
'data': rule.query,
|
||||||
|
}
|
||||||
|
|
||||||
|
try:
|
||||||
|
r = requests.post(**request)
|
||||||
|
except requests.ConnectionError as e:
|
||||||
|
raise GnocchiUnavailable(e)
|
||||||
|
if r.status_code // 200 != 1:
|
||||||
|
raise base.ClientSideError(r.content, status_code=r.status_code)
|
||||||
|
|
||||||
|
|
||||||
|
class AggregationMetricsByIdLookupRule(AlarmGnocchiThresholdRule):
|
||||||
metrics = wsme.wsattr([wtypes.text], mandatory=True)
|
metrics = wsme.wsattr([wtypes.text], mandatory=True)
|
||||||
"A list of metric Ids"
|
"A list of metric Ids"
|
||||||
|
|
||||||
|
@ -83,11 +83,11 @@ class TestGnocchiThresholdEvaluate(base.TestEvaluatorBase):
|
|||||||
granularity=60,
|
granularity=60,
|
||||||
metric='cpu_util',
|
metric='cpu_util',
|
||||||
resource_type='instance',
|
resource_type='instance',
|
||||||
resource_constraint='my_instance')
|
resource_id='my_instance')
|
||||||
),
|
),
|
||||||
models.Alarm(name='group_running_idle',
|
models.Alarm(name='group_running_idle',
|
||||||
description='group_running_idle',
|
description='group_running_idle',
|
||||||
type='gnocchi_metrics_threshold',
|
type='gnocchi_aggregation_by_metrics_threshold',
|
||||||
enabled=True,
|
enabled=True,
|
||||||
user_id='foobar',
|
user_id='foobar',
|
||||||
project_id='snafu',
|
project_id='snafu',
|
||||||
@ -109,6 +109,33 @@ class TestGnocchiThresholdEvaluate(base.TestEvaluatorBase):
|
|||||||
metrics=['0bb1604d-1193-4c0a-b4b8-74b170e35e83',
|
metrics=['0bb1604d-1193-4c0a-b4b8-74b170e35e83',
|
||||||
'9ddc209f-42f8-41e1-b8f1-8804f59c4053']),
|
'9ddc209f-42f8-41e1-b8f1-8804f59c4053']),
|
||||||
),
|
),
|
||||||
|
models.Alarm(name='instance_not_running',
|
||||||
|
description='instance_running_hot',
|
||||||
|
type='gnocchi_aggregation_by_resources_threshold',
|
||||||
|
enabled=True,
|
||||||
|
user_id='foobar',
|
||||||
|
project_id='snafu',
|
||||||
|
alarm_id=str(uuid.uuid4()),
|
||||||
|
state='insufficient data',
|
||||||
|
state_timestamp=constants.MIN_DATETIME,
|
||||||
|
timestamp=constants.MIN_DATETIME,
|
||||||
|
insufficient_data_actions=[],
|
||||||
|
ok_actions=[],
|
||||||
|
alarm_actions=[],
|
||||||
|
repeat_actions=False,
|
||||||
|
time_constraints=[],
|
||||||
|
rule=dict(
|
||||||
|
comparison_operator='gt',
|
||||||
|
threshold=80.0,
|
||||||
|
evaluation_periods=6,
|
||||||
|
aggregation_method='mean',
|
||||||
|
granularity=50,
|
||||||
|
metric='cpu_util',
|
||||||
|
resource_type='instance',
|
||||||
|
query='{"=": {"server_group": '
|
||||||
|
'"my_autoscaling_group"}}')
|
||||||
|
),
|
||||||
|
|
||||||
]
|
]
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
@ -132,10 +159,13 @@ class TestGnocchiThresholdEvaluate(base.TestEvaluatorBase):
|
|||||||
for v in moves.xrange(5)])
|
for v in moves.xrange(5)])
|
||||||
maxs = self._get_stats(300, [self.alarms[1].rule['threshold'] + v
|
maxs = self._get_stats(300, [self.alarms[1].rule['threshold'] + v
|
||||||
for v in moves.xrange(1, 4)])
|
for v in moves.xrange(1, 4)])
|
||||||
|
avgs2 = self._get_stats(50, [self.alarms[2].rule['threshold'] - v
|
||||||
|
for v in moves.xrange(6)])
|
||||||
self.requests.get.side_effect = [Exception('boom'),
|
self.requests.get.side_effect = [Exception('boom'),
|
||||||
FakeResponse(500, "error"),
|
FakeResponse(500, "error"),
|
||||||
means,
|
means,
|
||||||
maxs]
|
maxs]
|
||||||
|
self.requests.post.side_effect = [FakeResponse(500, "error"), avgs2]
|
||||||
self._evaluate_all_alarms()
|
self._evaluate_all_alarms()
|
||||||
self._assert_all_alarms('insufficient data')
|
self._assert_all_alarms('insufficient data')
|
||||||
self._evaluate_all_alarms()
|
self._evaluate_all_alarms()
|
||||||
@ -144,6 +174,7 @@ class TestGnocchiThresholdEvaluate(base.TestEvaluatorBase):
|
|||||||
def test_simple_insufficient(self):
|
def test_simple_insufficient(self):
|
||||||
self._set_all_alarms('ok')
|
self._set_all_alarms('ok')
|
||||||
self.requests.get.return_value = FakeResponse(200, [])
|
self.requests.get.return_value = FakeResponse(200, [])
|
||||||
|
self.requests.post.return_value = FakeResponse(200, [])
|
||||||
self._evaluate_all_alarms()
|
self._evaluate_all_alarms()
|
||||||
self._assert_all_alarms('insufficient data')
|
self._assert_all_alarms('insufficient data')
|
||||||
expected = [mock.call(alarm.alarm_id, state='insufficient data')
|
expected = [mock.call(alarm.alarm_id, state='insufficient data')
|
||||||
@ -169,8 +200,11 @@ class TestGnocchiThresholdEvaluate(base.TestEvaluatorBase):
|
|||||||
for v in moves.xrange(1, 6)])
|
for v in moves.xrange(1, 6)])
|
||||||
maxs = self._get_stats(300, [self.alarms[1].rule['threshold'] - v
|
maxs = self._get_stats(300, [self.alarms[1].rule['threshold'] - v
|
||||||
for v in moves.xrange(4)])
|
for v in moves.xrange(4)])
|
||||||
|
avgs2 = self._get_stats(50, [self.alarms[2].rule['threshold'] + v
|
||||||
|
for v in moves.xrange(1, 7)])
|
||||||
|
|
||||||
self.requests.get.side_effect = [avgs, maxs]
|
self.requests.get.side_effect = [avgs, maxs]
|
||||||
|
self.requests.post.side_effect = [avgs2]
|
||||||
self._evaluate_all_alarms()
|
self._evaluate_all_alarms()
|
||||||
|
|
||||||
expected_headers = {'X-Auth-Token': 'fake_token',
|
expected_headers = {'X-Auth-Token': 'fake_token',
|
||||||
@ -178,19 +212,33 @@ class TestGnocchiThresholdEvaluate(base.TestEvaluatorBase):
|
|||||||
|
|
||||||
start_alarm1 = "2015-01-26T12:51:00"
|
start_alarm1 = "2015-01-26T12:51:00"
|
||||||
start_alarm2 = "2015-01-26T12:32:00"
|
start_alarm2 = "2015-01-26T12:32:00"
|
||||||
|
start_alarm3 = "2015-01-26T12:51:10"
|
||||||
end = "2015-01-26T12:57:00"
|
end = "2015-01-26T12:57:00"
|
||||||
|
|
||||||
self.assertEqual([
|
self.assertEqual([
|
||||||
mock.call('http://localhost:8041/v1/resource/instance/my_instance/'
|
mock.call(url='http://localhost:8041/v1/resource/instance/'
|
||||||
'metric/cpu_util/measures?aggregation=mean'
|
'my_instance/metric/cpu_util/measures',
|
||||||
'&start=' + start_alarm1 + '&end=' + end,
|
params={'aggregation': 'mean',
|
||||||
|
'start': start_alarm1, 'end': end},
|
||||||
headers=expected_headers),
|
headers=expected_headers),
|
||||||
mock.call('http://localhost:8041/v1/metric_aggregation/?'
|
mock.call(url='http://localhost:8041/v1/aggregation/metric',
|
||||||
'aggregation=max&start=' + start_alarm2 + '&end=' + end +
|
params={'aggregation': 'max',
|
||||||
'&metric=0bb1604d-1193-4c0a-b4b8-74b170e35e83'
|
'start': start_alarm2, 'end': end,
|
||||||
'&metric=9ddc209f-42f8-41e1-b8f1-8804f59c4053',
|
'metric[]': [
|
||||||
|
'0bb1604d-1193-4c0a-b4b8-74b170e35e83',
|
||||||
|
'9ddc209f-42f8-41e1-b8f1-8804f59c4053']},
|
||||||
headers=expected_headers)],
|
headers=expected_headers)],
|
||||||
|
|
||||||
self.requests.get.mock_calls)
|
self.requests.get.mock_calls)
|
||||||
|
self.assertEqual([
|
||||||
|
mock.call(url='http://localhost:8041/v1/aggregation/resource/'
|
||||||
|
'instance/metric/cpu_util',
|
||||||
|
params={'aggregation': 'mean',
|
||||||
|
'start': start_alarm3, 'end': end},
|
||||||
|
data='{"=": {"server_group": "my_autoscaling_group"}}',
|
||||||
|
headers=expected_headers),
|
||||||
|
],
|
||||||
|
self.requests.post.mock_calls)
|
||||||
|
|
||||||
self._assert_all_alarms('alarm')
|
self._assert_all_alarms('alarm')
|
||||||
expected = [mock.call(alarm.alarm_id, state='alarm')
|
expected = [mock.call(alarm.alarm_id, state='alarm')
|
||||||
@ -200,9 +248,13 @@ class TestGnocchiThresholdEvaluate(base.TestEvaluatorBase):
|
|||||||
reasons = ['Transition to alarm due to 5 samples outside'
|
reasons = ['Transition to alarm due to 5 samples outside'
|
||||||
' threshold, most recent: %s' % avgs.values[-1],
|
' threshold, most recent: %s' % avgs.values[-1],
|
||||||
'Transition to alarm due to 4 samples outside'
|
'Transition to alarm due to 4 samples outside'
|
||||||
' threshold, most recent: %s' % maxs.values[-1]]
|
' threshold, most recent: %s' % maxs.values[-1],
|
||||||
|
'Transition to alarm due to 6 samples outside'
|
||||||
|
' threshold, most recent: %s' % avgs2.values[-1],
|
||||||
|
]
|
||||||
reason_datas = [self._reason_data('outside', 5, avgs.values[-1]),
|
reason_datas = [self._reason_data('outside', 5, avgs.values[-1]),
|
||||||
self._reason_data('outside', 4, maxs.values[-1])]
|
self._reason_data('outside', 4, maxs.values[-1]),
|
||||||
|
self._reason_data('outside', 6, avgs2.values[-1])]
|
||||||
expected = [mock.call(alarm, 'ok', reason, reason_data)
|
expected = [mock.call(alarm, 'ok', reason, reason_data)
|
||||||
for alarm, reason, reason_data
|
for alarm, reason, reason_data
|
||||||
in zip(self.alarms, reasons, reason_datas)]
|
in zip(self.alarms, reasons, reason_datas)]
|
||||||
@ -214,6 +266,9 @@ class TestGnocchiThresholdEvaluate(base.TestEvaluatorBase):
|
|||||||
for v in moves.xrange(5)])
|
for v in moves.xrange(5)])
|
||||||
maxs = self._get_stats(300, [self.alarms[1].rule['threshold'] + v
|
maxs = self._get_stats(300, [self.alarms[1].rule['threshold'] + v
|
||||||
for v in moves.xrange(1, 5)])
|
for v in moves.xrange(1, 5)])
|
||||||
|
avgs2 = self._get_stats(50, [self.alarms[2].rule['threshold'] - v
|
||||||
|
for v in moves.xrange(6)])
|
||||||
|
self.requests.post.side_effect = [avgs2]
|
||||||
self.requests.get.side_effect = [avgs, maxs]
|
self.requests.get.side_effect = [avgs, maxs]
|
||||||
self._evaluate_all_alarms()
|
self._evaluate_all_alarms()
|
||||||
self._assert_all_alarms('ok')
|
self._assert_all_alarms('ok')
|
||||||
@ -224,9 +279,12 @@ class TestGnocchiThresholdEvaluate(base.TestEvaluatorBase):
|
|||||||
reasons = ['Transition to ok due to 5 samples inside'
|
reasons = ['Transition to ok due to 5 samples inside'
|
||||||
' threshold, most recent: %s' % avgs.values[-1],
|
' threshold, most recent: %s' % avgs.values[-1],
|
||||||
'Transition to ok due to 4 samples inside'
|
'Transition to ok due to 4 samples inside'
|
||||||
' threshold, most recent: %s' % maxs.values[-1]]
|
' threshold, most recent: %s' % maxs.values[-1],
|
||||||
|
'Transition to ok due to 6 samples inside'
|
||||||
|
' threshold, most recent: %s' % avgs2.values[-1]]
|
||||||
reason_datas = [self._reason_data('inside', 5, avgs.values[-1]),
|
reason_datas = [self._reason_data('inside', 5, avgs.values[-1]),
|
||||||
self._reason_data('inside', 4, maxs.values[-1])]
|
self._reason_data('inside', 4, maxs.values[-1]),
|
||||||
|
self._reason_data('inside', 6, avgs2.values[-1])]
|
||||||
expected = [mock.call(alarm, 'alarm', reason, reason_data)
|
expected = [mock.call(alarm, 'alarm', reason, reason_data)
|
||||||
for alarm, reason, reason_data
|
for alarm, reason, reason_data
|
||||||
in zip(self.alarms, reasons, reason_datas)]
|
in zip(self.alarms, reasons, reason_datas)]
|
||||||
@ -238,6 +296,9 @@ class TestGnocchiThresholdEvaluate(base.TestEvaluatorBase):
|
|||||||
for v in moves.xrange(5)])
|
for v in moves.xrange(5)])
|
||||||
maxs = self._get_stats(300, [self.alarms[1].rule['threshold'] - v
|
maxs = self._get_stats(300, [self.alarms[1].rule['threshold'] - v
|
||||||
for v in moves.xrange(-1, 3)])
|
for v in moves.xrange(-1, 3)])
|
||||||
|
avgs2 = self._get_stats(50, [self.alarms[2].rule['threshold'] + v
|
||||||
|
for v in moves.xrange(6)])
|
||||||
|
self.requests.post.side_effect = [avgs2]
|
||||||
self.requests.get.side_effect = [avgs, maxs]
|
self.requests.get.side_effect = [avgs, maxs]
|
||||||
self._evaluate_all_alarms()
|
self._evaluate_all_alarms()
|
||||||
self._assert_all_alarms('ok')
|
self._assert_all_alarms('ok')
|
||||||
@ -253,6 +314,9 @@ class TestGnocchiThresholdEvaluate(base.TestEvaluatorBase):
|
|||||||
for v in moves.xrange(5)])
|
for v in moves.xrange(5)])
|
||||||
maxs = self._get_stats(300, [self.alarms[1].rule['threshold'] - v
|
maxs = self._get_stats(300, [self.alarms[1].rule['threshold'] - v
|
||||||
for v in moves.xrange(-1, 3)])
|
for v in moves.xrange(-1, 3)])
|
||||||
|
avgs2 = self._get_stats(50, [self.alarms[2].rule['threshold'] + v
|
||||||
|
for v in moves.xrange(6)])
|
||||||
|
self.requests.post.side_effect = [avgs2]
|
||||||
self.requests.get.side_effect = [avgs, maxs]
|
self.requests.get.side_effect = [avgs, maxs]
|
||||||
self._evaluate_all_alarms()
|
self._evaluate_all_alarms()
|
||||||
self._assert_all_alarms('ok')
|
self._assert_all_alarms('ok')
|
||||||
@ -270,6 +334,9 @@ class TestGnocchiThresholdEvaluate(base.TestEvaluatorBase):
|
|||||||
for v in moves.xrange(1, 6)])
|
for v in moves.xrange(1, 6)])
|
||||||
maxs = self._get_stats(300, [self.alarms[1].rule['threshold'] - v
|
maxs = self._get_stats(300, [self.alarms[1].rule['threshold'] - v
|
||||||
for v in moves.xrange(4)])
|
for v in moves.xrange(4)])
|
||||||
|
avgs2 = self._get_stats(50, [self.alarms[2].rule['threshold'] + v
|
||||||
|
for v in moves.xrange(6)])
|
||||||
|
self.requests.post.side_effect = [avgs2]
|
||||||
self.requests.get.side_effect = [avgs, maxs]
|
self.requests.get.side_effect = [avgs, maxs]
|
||||||
self._evaluate_all_alarms()
|
self._evaluate_all_alarms()
|
||||||
self._assert_all_alarms('alarm')
|
self._assert_all_alarms('alarm')
|
||||||
@ -289,6 +356,9 @@ class TestGnocchiThresholdEvaluate(base.TestEvaluatorBase):
|
|||||||
for v in moves.xrange(1, 6)])
|
for v in moves.xrange(1, 6)])
|
||||||
maxs = self._get_stats(300, [self.alarms[1].rule['threshold'] - v
|
maxs = self._get_stats(300, [self.alarms[1].rule['threshold'] - v
|
||||||
for v in moves.xrange(4)])
|
for v in moves.xrange(4)])
|
||||||
|
avgs2 = self._get_stats(50, [self.alarms[2].rule['threshold'] + v
|
||||||
|
for v in moves.xrange(1, 7)])
|
||||||
|
self.requests.post.side_effect = [avgs2]
|
||||||
self.requests.get.side_effect = [avgs, maxs]
|
self.requests.get.side_effect = [avgs, maxs]
|
||||||
self._evaluate_all_alarms()
|
self._evaluate_all_alarms()
|
||||||
self._assert_all_alarms('alarm')
|
self._assert_all_alarms('alarm')
|
||||||
@ -299,9 +369,12 @@ class TestGnocchiThresholdEvaluate(base.TestEvaluatorBase):
|
|||||||
reasons = ['Transition to alarm due to 5 samples outside'
|
reasons = ['Transition to alarm due to 5 samples outside'
|
||||||
' threshold, most recent: %s' % avgs.values[-1],
|
' threshold, most recent: %s' % avgs.values[-1],
|
||||||
'Transition to alarm due to 4 samples outside'
|
'Transition to alarm due to 4 samples outside'
|
||||||
' threshold, most recent: %s' % maxs.values[-1]]
|
' threshold, most recent: %s' % maxs.values[-1],
|
||||||
|
'Transition to alarm due to 6 samples outside'
|
||||||
|
' threshold, most recent: %s' % avgs2.values[-1]]
|
||||||
reason_datas = [self._reason_data('outside', 5, avgs.values[-1]),
|
reason_datas = [self._reason_data('outside', 5, avgs.values[-1]),
|
||||||
self._reason_data('outside', 4, maxs.values[-1])]
|
self._reason_data('outside', 4, maxs.values[-1]),
|
||||||
|
self._reason_data('outside', 6, avgs2.values[-1])]
|
||||||
expected = [mock.call(alarm, 'ok', reason, reason_data)
|
expected = [mock.call(alarm, 'ok', reason, reason_data)
|
||||||
for alarm, reason, reason_data
|
for alarm, reason, reason_data
|
||||||
in zip(self.alarms, reasons, reason_datas)]
|
in zip(self.alarms, reasons, reason_datas)]
|
||||||
@ -313,6 +386,9 @@ class TestGnocchiThresholdEvaluate(base.TestEvaluatorBase):
|
|||||||
for v in moves.xrange(1, 6)])
|
for v in moves.xrange(1, 6)])
|
||||||
maxs = self._get_stats(300, [self.alarms[1].rule['threshold'] - v
|
maxs = self._get_stats(300, [self.alarms[1].rule['threshold'] - v
|
||||||
for v in moves.xrange(4)])
|
for v in moves.xrange(4)])
|
||||||
|
avgs2 = self._get_stats(50, [self.alarms[2].rule['threshold'] + v
|
||||||
|
for v in moves.xrange(1, 7)])
|
||||||
|
self.requests.post.side_effect = [avgs2]
|
||||||
self.requests.get.side_effect = [avgs, maxs]
|
self.requests.get.side_effect = [avgs, maxs]
|
||||||
self._evaluate_all_alarms()
|
self._evaluate_all_alarms()
|
||||||
self._assert_all_alarms('alarm')
|
self._assert_all_alarms('alarm')
|
||||||
@ -323,9 +399,12 @@ class TestGnocchiThresholdEvaluate(base.TestEvaluatorBase):
|
|||||||
reasons = ['Transition to alarm due to 5 samples outside'
|
reasons = ['Transition to alarm due to 5 samples outside'
|
||||||
' threshold, most recent: %s' % avgs.values[-1],
|
' threshold, most recent: %s' % avgs.values[-1],
|
||||||
'Transition to alarm due to 4 samples outside'
|
'Transition to alarm due to 4 samples outside'
|
||||||
' threshold, most recent: %s' % maxs.values[-1]]
|
' threshold, most recent: %s' % maxs.values[-1],
|
||||||
|
'Transition to alarm due to 6 samples outside'
|
||||||
|
' threshold, most recent: %s' % avgs2.values[-1]]
|
||||||
reason_datas = [self._reason_data('outside', 5, avgs.values[-1]),
|
reason_datas = [self._reason_data('outside', 5, avgs.values[-1]),
|
||||||
self._reason_data('outside', 4, maxs.values[-1])]
|
self._reason_data('outside', 4, maxs.values[-1]),
|
||||||
|
self._reason_data('outside', 6, avgs2.values[-1])]
|
||||||
expected = [mock.call(alarm, 'insufficient data',
|
expected = [mock.call(alarm, 'insufficient data',
|
||||||
reason, reason_data)
|
reason, reason_data)
|
||||||
for alarm, reason, reason_data
|
for alarm, reason, reason_data
|
||||||
@ -345,6 +424,7 @@ class TestGnocchiThresholdEvaluate(base.TestEvaluatorBase):
|
|||||||
'timezone': 'Europe/Ljubljana'}
|
'timezone': 'Europe/Ljubljana'}
|
||||||
]
|
]
|
||||||
self.alarms[1].time_constraints = self.alarms[0].time_constraints
|
self.alarms[1].time_constraints = self.alarms[0].time_constraints
|
||||||
|
self.alarms[2].time_constraints = self.alarms[0].time_constraints
|
||||||
dt = datetime.datetime(2014, 1, 1, 15, 0, 0,
|
dt = datetime.datetime(2014, 1, 1, 15, 0, 0,
|
||||||
tzinfo=pytz.timezone('Europe/Ljubljana'))
|
tzinfo=pytz.timezone('Europe/Ljubljana'))
|
||||||
mock_utcnow.return_value = dt.astimezone(pytz.UTC)
|
mock_utcnow.return_value = dt.astimezone(pytz.UTC)
|
||||||
|
@ -172,12 +172,12 @@ class TestAlarms(v2.FunctionalTest,
|
|||||||
evaluation_periods=1,
|
evaluation_periods=1,
|
||||||
metric='meter.test',
|
metric='meter.test',
|
||||||
resource_type='instance',
|
resource_type='instance',
|
||||||
resource_constraint=(
|
resource_id=(
|
||||||
'6841c175-d7c4-4bc2-bc7a-1c7832271b8f'),
|
'6841c175-d7c4-4bc2-bc7a-1c7832271b8f'),
|
||||||
)
|
)
|
||||||
),
|
),
|
||||||
models.Alarm(name='name6',
|
models.Alarm(name='name6',
|
||||||
type='gnocchi_metrics_threshold',
|
type='gnocchi_aggregation_by_metrics_threshold',
|
||||||
enabled=True,
|
enabled=True,
|
||||||
alarm_id='f',
|
alarm_id='f',
|
||||||
description='f',
|
description='f',
|
||||||
@ -202,6 +202,33 @@ class TestAlarms(v2.FunctionalTest,
|
|||||||
'a1fb80f4-c242-4f57-87c6-68f47521059e']
|
'a1fb80f4-c242-4f57-87c6-68f47521059e']
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
models.Alarm(name='name7',
|
||||||
|
type='gnocchi_aggregation_by_resources_threshold',
|
||||||
|
enabled=True,
|
||||||
|
alarm_id='g',
|
||||||
|
description='f',
|
||||||
|
state='insufficient data',
|
||||||
|
severity='critical',
|
||||||
|
state_timestamp=constants.MIN_DATETIME,
|
||||||
|
timestamp=constants.MIN_DATETIME,
|
||||||
|
ok_actions=[],
|
||||||
|
insufficient_data_actions=[],
|
||||||
|
alarm_actions=[],
|
||||||
|
repeat_actions=True,
|
||||||
|
user_id=self.auth_headers['X-User-Id'],
|
||||||
|
project_id=self.auth_headers['X-Project-Id'],
|
||||||
|
time_constraints=[],
|
||||||
|
rule=dict(comparison_operator='gt',
|
||||||
|
threshold=2.0,
|
||||||
|
aggregation_method='mean',
|
||||||
|
granularity=60,
|
||||||
|
evaluation_periods=1,
|
||||||
|
metric='meter.test',
|
||||||
|
resource_type='instance',
|
||||||
|
query='{"=": {"server_group": '
|
||||||
|
'"my_autoscaling_group"}}')
|
||||||
|
),
|
||||||
|
|
||||||
]:
|
]:
|
||||||
|
|
||||||
self.alarm_conn.update_alarm(alarm)
|
self.alarm_conn.update_alarm(alarm)
|
||||||
@ -225,9 +252,9 @@ class TestAlarms(v2.FunctionalTest,
|
|||||||
|
|
||||||
def test_list_alarms(self):
|
def test_list_alarms(self):
|
||||||
data = self.get_json('/alarms')
|
data = self.get_json('/alarms')
|
||||||
self.assertEqual(6, len(data))
|
self.assertEqual(7, len(data))
|
||||||
self.assertEqual(set(['name1', 'name2', 'name3', 'name4', 'name5',
|
self.assertEqual(set(['name1', 'name2', 'name3', 'name4', 'name5',
|
||||||
'name6']),
|
'name6', 'name7']),
|
||||||
set(r['name'] for r in data))
|
set(r['name'] for r in data))
|
||||||
self.assertEqual(set(['meter.test', 'meter.mine']),
|
self.assertEqual(set(['meter.test', 'meter.mine']),
|
||||||
set(r['threshold_rule']['meter_name']
|
set(r['threshold_rule']['meter_name']
|
||||||
@ -403,7 +430,7 @@ class TestAlarms(v2.FunctionalTest,
|
|||||||
q=[{'field': field,
|
q=[{'field': field,
|
||||||
'op': 'eq',
|
'op': 'eq',
|
||||||
'value': project}])
|
'value': project}])
|
||||||
self.assertEqual(6, len(alarms))
|
self.assertEqual(7, len(alarms))
|
||||||
|
|
||||||
_test('project')
|
_test('project')
|
||||||
_test('project_id')
|
_test('project_id')
|
||||||
@ -468,7 +495,7 @@ class TestAlarms(v2.FunctionalTest,
|
|||||||
% field.split('/', 1)[-1],
|
% field.split('/', 1)[-1],
|
||||||
resp.json['error_message']['faultstring'])
|
resp.json['error_message']['faultstring'])
|
||||||
alarms = list(self.alarm_conn.get_alarms())
|
alarms = list(self.alarm_conn.get_alarms())
|
||||||
self.assertEqual(6, len(alarms))
|
self.assertEqual(7, len(alarms))
|
||||||
|
|
||||||
def test_post_invalid_alarm_time_constraint_start(self):
|
def test_post_invalid_alarm_time_constraint_start(self):
|
||||||
json = {
|
json = {
|
||||||
@ -489,7 +516,7 @@ class TestAlarms(v2.FunctionalTest,
|
|||||||
self.post_json('/alarms', params=json, expect_errors=True, status=400,
|
self.post_json('/alarms', params=json, expect_errors=True, status=400,
|
||||||
headers=self.auth_headers)
|
headers=self.auth_headers)
|
||||||
alarms = list(self.alarm_conn.get_alarms())
|
alarms = list(self.alarm_conn.get_alarms())
|
||||||
self.assertEqual(6, len(alarms))
|
self.assertEqual(7, len(alarms))
|
||||||
|
|
||||||
def test_post_duplicate_time_constraint_name(self):
|
def test_post_duplicate_time_constraint_name(self):
|
||||||
json = {
|
json = {
|
||||||
@ -518,7 +545,7 @@ class TestAlarms(v2.FunctionalTest,
|
|||||||
"Time constraint names must be unique for a given alarm.",
|
"Time constraint names must be unique for a given alarm.",
|
||||||
resp.json['error_message']['faultstring'])
|
resp.json['error_message']['faultstring'])
|
||||||
alarms = list(self.alarm_conn.get_alarms())
|
alarms = list(self.alarm_conn.get_alarms())
|
||||||
self.assertEqual(6, len(alarms))
|
self.assertEqual(7, len(alarms))
|
||||||
|
|
||||||
def test_post_alarm_null_time_constraint(self):
|
def test_post_alarm_null_time_constraint(self):
|
||||||
json = {
|
json = {
|
||||||
@ -552,7 +579,7 @@ class TestAlarms(v2.FunctionalTest,
|
|||||||
self.post_json('/alarms', params=json, expect_errors=True, status=400,
|
self.post_json('/alarms', params=json, expect_errors=True, status=400,
|
||||||
headers=self.auth_headers)
|
headers=self.auth_headers)
|
||||||
alarms = list(self.alarm_conn.get_alarms())
|
alarms = list(self.alarm_conn.get_alarms())
|
||||||
self.assertEqual(6, len(alarms))
|
self.assertEqual(7, len(alarms))
|
||||||
|
|
||||||
def test_post_invalid_alarm_time_constraint_timezone(self):
|
def test_post_invalid_alarm_time_constraint_timezone(self):
|
||||||
json = {
|
json = {
|
||||||
@ -574,7 +601,7 @@ class TestAlarms(v2.FunctionalTest,
|
|||||||
self.post_json('/alarms', params=json, expect_errors=True, status=400,
|
self.post_json('/alarms', params=json, expect_errors=True, status=400,
|
||||||
headers=self.auth_headers)
|
headers=self.auth_headers)
|
||||||
alarms = list(self.alarm_conn.get_alarms())
|
alarms = list(self.alarm_conn.get_alarms())
|
||||||
self.assertEqual(6, len(alarms))
|
self.assertEqual(7, len(alarms))
|
||||||
|
|
||||||
def test_post_invalid_alarm_period(self):
|
def test_post_invalid_alarm_period(self):
|
||||||
json = {
|
json = {
|
||||||
@ -592,7 +619,7 @@ class TestAlarms(v2.FunctionalTest,
|
|||||||
self.post_json('/alarms', params=json, expect_errors=True, status=400,
|
self.post_json('/alarms', params=json, expect_errors=True, status=400,
|
||||||
headers=self.auth_headers)
|
headers=self.auth_headers)
|
||||||
alarms = list(self.alarm_conn.get_alarms())
|
alarms = list(self.alarm_conn.get_alarms())
|
||||||
self.assertEqual(6, len(alarms))
|
self.assertEqual(7, len(alarms))
|
||||||
|
|
||||||
def test_post_null_threshold_rule(self):
|
def test_post_null_threshold_rule(self):
|
||||||
json = {
|
json = {
|
||||||
@ -625,7 +652,7 @@ class TestAlarms(v2.FunctionalTest,
|
|||||||
self.assertIn(expected_err_msg,
|
self.assertIn(expected_err_msg,
|
||||||
resp.json['error_message']['faultstring'])
|
resp.json['error_message']['faultstring'])
|
||||||
alarms = list(self.alarm_conn.get_alarms())
|
alarms = list(self.alarm_conn.get_alarms())
|
||||||
self.assertEqual(6, len(alarms))
|
self.assertEqual(7, len(alarms))
|
||||||
|
|
||||||
def test_post_invalid_alarm_input_state(self):
|
def test_post_invalid_alarm_input_state(self):
|
||||||
json = {
|
json = {
|
||||||
@ -645,7 +672,7 @@ class TestAlarms(v2.FunctionalTest,
|
|||||||
self.assertIn(expected_err_msg,
|
self.assertIn(expected_err_msg,
|
||||||
resp.json['error_message']['faultstring'])
|
resp.json['error_message']['faultstring'])
|
||||||
alarms = list(self.alarm_conn.get_alarms())
|
alarms = list(self.alarm_conn.get_alarms())
|
||||||
self.assertEqual(6, len(alarms))
|
self.assertEqual(7, len(alarms))
|
||||||
|
|
||||||
def test_post_invalid_alarm_input_severity(self):
|
def test_post_invalid_alarm_input_severity(self):
|
||||||
json = {
|
json = {
|
||||||
@ -666,7 +693,7 @@ class TestAlarms(v2.FunctionalTest,
|
|||||||
self.assertIn(expected_err_msg,
|
self.assertIn(expected_err_msg,
|
||||||
resp.json['error_message']['faultstring'])
|
resp.json['error_message']['faultstring'])
|
||||||
alarms = list(self.alarm_conn.get_alarms())
|
alarms = list(self.alarm_conn.get_alarms())
|
||||||
self.assertEqual(6, len(alarms))
|
self.assertEqual(7, len(alarms))
|
||||||
|
|
||||||
def test_post_invalid_alarm_input_comparison_operator(self):
|
def test_post_invalid_alarm_input_comparison_operator(self):
|
||||||
json = {
|
json = {
|
||||||
@ -687,7 +714,7 @@ class TestAlarms(v2.FunctionalTest,
|
|||||||
self.assertIn(expected_err_msg,
|
self.assertIn(expected_err_msg,
|
||||||
resp.json['error_message']['faultstring'])
|
resp.json['error_message']['faultstring'])
|
||||||
alarms = list(self.alarm_conn.get_alarms())
|
alarms = list(self.alarm_conn.get_alarms())
|
||||||
self.assertEqual(6, len(alarms))
|
self.assertEqual(7, len(alarms))
|
||||||
|
|
||||||
def test_post_invalid_alarm_input_type(self):
|
def test_post_invalid_alarm_input_type(self):
|
||||||
json = {
|
json = {
|
||||||
@ -708,7 +735,7 @@ class TestAlarms(v2.FunctionalTest,
|
|||||||
self.assertIn(expected_err_msg,
|
self.assertIn(expected_err_msg,
|
||||||
resp.json['error_message']['faultstring'])
|
resp.json['error_message']['faultstring'])
|
||||||
alarms = list(self.alarm_conn.get_alarms())
|
alarms = list(self.alarm_conn.get_alarms())
|
||||||
self.assertEqual(6, len(alarms))
|
self.assertEqual(7, len(alarms))
|
||||||
|
|
||||||
def test_post_invalid_alarm_input_enabled_str(self):
|
def test_post_invalid_alarm_input_enabled_str(self):
|
||||||
json = {
|
json = {
|
||||||
@ -732,7 +759,7 @@ class TestAlarms(v2.FunctionalTest,
|
|||||||
self.assertEqual(expected_err_msg,
|
self.assertEqual(expected_err_msg,
|
||||||
resp.json['error_message']['faultstring'])
|
resp.json['error_message']['faultstring'])
|
||||||
alarms = list(self.alarm_conn.get_alarms())
|
alarms = list(self.alarm_conn.get_alarms())
|
||||||
self.assertEqual(6, len(alarms))
|
self.assertEqual(7, len(alarms))
|
||||||
|
|
||||||
def test_post_invalid_alarm_input_enabled_int(self):
|
def test_post_invalid_alarm_input_enabled_int(self):
|
||||||
json = {
|
json = {
|
||||||
@ -756,7 +783,7 @@ class TestAlarms(v2.FunctionalTest,
|
|||||||
self.assertEqual(expected_err_msg,
|
self.assertEqual(expected_err_msg,
|
||||||
resp.json['error_message']['faultstring'])
|
resp.json['error_message']['faultstring'])
|
||||||
alarms = list(self.alarm_conn.get_alarms())
|
alarms = list(self.alarm_conn.get_alarms())
|
||||||
self.assertEqual(6, len(alarms))
|
self.assertEqual(7, len(alarms))
|
||||||
|
|
||||||
def test_post_invalid_combination_alarm_input_operator(self):
|
def test_post_invalid_combination_alarm_input_operator(self):
|
||||||
json = {
|
json = {
|
||||||
@ -782,7 +809,7 @@ class TestAlarms(v2.FunctionalTest,
|
|||||||
self.assertIn(expected_err_msg,
|
self.assertIn(expected_err_msg,
|
||||||
resp.json['error_message']['faultstring'])
|
resp.json['error_message']['faultstring'])
|
||||||
alarms = list(self.alarm_conn.get_alarms())
|
alarms = list(self.alarm_conn.get_alarms())
|
||||||
self.assertEqual(6, len(alarms))
|
self.assertEqual(7, len(alarms))
|
||||||
|
|
||||||
def test_post_invalid_alarm_query(self):
|
def test_post_invalid_alarm_query(self):
|
||||||
json = {
|
json = {
|
||||||
@ -801,7 +828,7 @@ class TestAlarms(v2.FunctionalTest,
|
|||||||
self.post_json('/alarms', params=json, expect_errors=True, status=400,
|
self.post_json('/alarms', params=json, expect_errors=True, status=400,
|
||||||
headers=self.auth_headers)
|
headers=self.auth_headers)
|
||||||
alarms = list(self.alarm_conn.get_alarms())
|
alarms = list(self.alarm_conn.get_alarms())
|
||||||
self.assertEqual(6, len(alarms))
|
self.assertEqual(7, len(alarms))
|
||||||
|
|
||||||
def test_post_invalid_alarm_query_field_type(self):
|
def test_post_invalid_alarm_query_field_type(self):
|
||||||
json = {
|
json = {
|
||||||
@ -825,7 +852,7 @@ class TestAlarms(v2.FunctionalTest,
|
|||||||
fault_string = resp_string['error_message']['faultstring']
|
fault_string = resp_string['error_message']['faultstring']
|
||||||
self.assertTrue(fault_string.startswith(expected_error_message))
|
self.assertTrue(fault_string.startswith(expected_error_message))
|
||||||
alarms = list(self.alarm_conn.get_alarms())
|
alarms = list(self.alarm_conn.get_alarms())
|
||||||
self.assertEqual(6, len(alarms))
|
self.assertEqual(7, len(alarms))
|
||||||
|
|
||||||
def test_post_invalid_alarm_query_non_field(self):
|
def test_post_invalid_alarm_query_non_field(self):
|
||||||
json = {
|
json = {
|
||||||
@ -845,7 +872,7 @@ class TestAlarms(v2.FunctionalTest,
|
|||||||
fault_string = resp.json['error_message']['faultstring']
|
fault_string = resp.json['error_message']['faultstring']
|
||||||
self.assertEqual(expected_error_message, fault_string)
|
self.assertEqual(expected_error_message, fault_string)
|
||||||
alarms = list(self.alarm_conn.get_alarms())
|
alarms = list(self.alarm_conn.get_alarms())
|
||||||
self.assertEqual(6, len(alarms))
|
self.assertEqual(7, len(alarms))
|
||||||
|
|
||||||
def test_post_invalid_alarm_query_non_value(self):
|
def test_post_invalid_alarm_query_non_value(self):
|
||||||
json = {
|
json = {
|
||||||
@ -865,7 +892,7 @@ class TestAlarms(v2.FunctionalTest,
|
|||||||
fault_string = resp.json['error_message']['faultstring']
|
fault_string = resp.json['error_message']['faultstring']
|
||||||
self.assertEqual(expected_error_message, fault_string)
|
self.assertEqual(expected_error_message, fault_string)
|
||||||
alarms = list(self.alarm_conn.get_alarms())
|
alarms = list(self.alarm_conn.get_alarms())
|
||||||
self.assertEqual(6, len(alarms))
|
self.assertEqual(7, len(alarms))
|
||||||
|
|
||||||
def test_post_invalid_alarm_have_multiple_rules(self):
|
def test_post_invalid_alarm_have_multiple_rules(self):
|
||||||
json = {
|
json = {
|
||||||
@ -886,7 +913,7 @@ class TestAlarms(v2.FunctionalTest,
|
|||||||
resp = self.post_json('/alarms', params=json, expect_errors=True,
|
resp = self.post_json('/alarms', params=json, expect_errors=True,
|
||||||
status=400, headers=self.auth_headers)
|
status=400, headers=self.auth_headers)
|
||||||
alarms = list(self.alarm_conn.get_alarms())
|
alarms = list(self.alarm_conn.get_alarms())
|
||||||
self.assertEqual(6, len(alarms))
|
self.assertEqual(7, len(alarms))
|
||||||
self.assertEqual('threshold_rule and combination_rule cannot '
|
self.assertEqual('threshold_rule and combination_rule cannot '
|
||||||
'be set at the same time',
|
'be set at the same time',
|
||||||
resp.json['error_message']['faultstring'])
|
resp.json['error_message']['faultstring'])
|
||||||
@ -910,7 +937,7 @@ class TestAlarms(v2.FunctionalTest,
|
|||||||
resp = self.post_json('/alarms', params=json, expect_errors=True,
|
resp = self.post_json('/alarms', params=json, expect_errors=True,
|
||||||
status=400, headers=self.auth_headers)
|
status=400, headers=self.auth_headers)
|
||||||
alarms = list(self.alarm_conn.get_alarms())
|
alarms = list(self.alarm_conn.get_alarms())
|
||||||
self.assertEqual(6, len(alarms))
|
self.assertEqual(7, len(alarms))
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
'Unknown argument: "timestamp": '
|
'Unknown argument: "timestamp": '
|
||||||
'not valid for this resource',
|
'not valid for this resource',
|
||||||
@ -949,7 +976,7 @@ class TestAlarms(v2.FunctionalTest,
|
|||||||
resp = self.post_json('/alarms', params=json, status=400,
|
resp = self.post_json('/alarms', params=json, status=400,
|
||||||
headers=self.auth_headers)
|
headers=self.auth_headers)
|
||||||
alarms = list(self.alarm_conn.get_alarms())
|
alarms = list(self.alarm_conn.get_alarms())
|
||||||
self.assertEqual(6, len(alarms))
|
self.assertEqual(7, len(alarms))
|
||||||
self.assertEqual(error_message,
|
self.assertEqual(error_message,
|
||||||
resp.json['error_message']['faultstring'])
|
resp.json['error_message']['faultstring'])
|
||||||
|
|
||||||
@ -1017,7 +1044,7 @@ class TestAlarms(v2.FunctionalTest,
|
|||||||
self.post_json('/alarms', params=json, status=201,
|
self.post_json('/alarms', params=json, status=201,
|
||||||
headers=self.auth_headers)
|
headers=self.auth_headers)
|
||||||
alarms = list(self.alarm_conn.get_alarms())
|
alarms = list(self.alarm_conn.get_alarms())
|
||||||
self.assertEqual(7, len(alarms))
|
self.assertEqual(8, len(alarms))
|
||||||
for alarm in alarms:
|
for alarm in alarms:
|
||||||
if alarm.name == 'added_alarm_defaults':
|
if alarm.name == 'added_alarm_defaults':
|
||||||
for key in to_check:
|
for key in to_check:
|
||||||
@ -1970,18 +1997,18 @@ class TestAlarms(v2.FunctionalTest,
|
|||||||
|
|
||||||
def test_delete_alarm(self):
|
def test_delete_alarm(self):
|
||||||
data = self.get_json('/alarms')
|
data = self.get_json('/alarms')
|
||||||
self.assertEqual(6, len(data))
|
self.assertEqual(7, len(data))
|
||||||
|
|
||||||
resp = self.delete('/alarms/%s' % data[0]['alarm_id'],
|
resp = self.delete('/alarms/%s' % data[0]['alarm_id'],
|
||||||
headers=self.auth_headers,
|
headers=self.auth_headers,
|
||||||
status=204)
|
status=204)
|
||||||
self.assertEqual('', resp.body)
|
self.assertEqual('', resp.body)
|
||||||
alarms = list(self.alarm_conn.get_alarms())
|
alarms = list(self.alarm_conn.get_alarms())
|
||||||
self.assertEqual(5, len(alarms))
|
self.assertEqual(6, len(alarms))
|
||||||
|
|
||||||
def test_get_state_alarm(self):
|
def test_get_state_alarm(self):
|
||||||
data = self.get_json('/alarms')
|
data = self.get_json('/alarms')
|
||||||
self.assertEqual(6, len(data))
|
self.assertEqual(7, len(data))
|
||||||
|
|
||||||
resp = self.get_json('/alarms/%s/state' % data[0]['alarm_id'],
|
resp = self.get_json('/alarms/%s/state' % data[0]['alarm_id'],
|
||||||
headers=self.auth_headers)
|
headers=self.auth_headers)
|
||||||
@ -1989,7 +2016,7 @@ class TestAlarms(v2.FunctionalTest,
|
|||||||
|
|
||||||
def test_set_state_alarm(self):
|
def test_set_state_alarm(self):
|
||||||
data = self.get_json('/alarms')
|
data = self.get_json('/alarms')
|
||||||
self.assertEqual(6, len(data))
|
self.assertEqual(7, len(data))
|
||||||
|
|
||||||
resp = self.put_json('/alarms/%s/state' % data[0]['alarm_id'],
|
resp = self.put_json('/alarms/%s/state' % data[0]['alarm_id'],
|
||||||
headers=self.auth_headers,
|
headers=self.auth_headers,
|
||||||
@ -2001,7 +2028,7 @@ class TestAlarms(v2.FunctionalTest,
|
|||||||
|
|
||||||
def test_set_invalid_state_alarm(self):
|
def test_set_invalid_state_alarm(self):
|
||||||
data = self.get_json('/alarms')
|
data = self.get_json('/alarms')
|
||||||
self.assertEqual(6, len(data))
|
self.assertEqual(7, len(data))
|
||||||
|
|
||||||
self.put_json('/alarms/%s/state' % data[0]['alarm_id'],
|
self.put_json('/alarms/%s/state' % data[0]['alarm_id'],
|
||||||
headers=self.auth_headers,
|
headers=self.auth_headers,
|
||||||
@ -2421,7 +2448,7 @@ class TestAlarms(v2.FunctionalTest,
|
|||||||
'evaluation_periods': 3,
|
'evaluation_periods': 3,
|
||||||
'granularity': 180,
|
'granularity': 180,
|
||||||
'resource_type': 'instance',
|
'resource_type': 'instance',
|
||||||
'resource_constraint': '209ef69c-c10c-4efb-90ff-46f4b2d90d2e',
|
'resource_id': '209ef69c-c10c-4efb-90ff-46f4b2d90d2e',
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2469,13 +2496,13 @@ class TestAlarms(v2.FunctionalTest,
|
|||||||
'enabled': False,
|
'enabled': False,
|
||||||
'name': 'name_post',
|
'name': 'name_post',
|
||||||
'state': 'ok',
|
'state': 'ok',
|
||||||
'type': 'gnocchi_metrics_threshold',
|
'type': 'gnocchi_aggregation_by_metrics_threshold',
|
||||||
'severity': 'critical',
|
'severity': 'critical',
|
||||||
'ok_actions': ['http://something/ok'],
|
'ok_actions': ['http://something/ok'],
|
||||||
'alarm_actions': ['http://something/alarm'],
|
'alarm_actions': ['http://something/alarm'],
|
||||||
'insufficient_data_actions': ['http://something/no'],
|
'insufficient_data_actions': ['http://something/no'],
|
||||||
'repeat_actions': True,
|
'repeat_actions': True,
|
||||||
'gnocchi_metrics_threshold_rule': {
|
'gnocchi_aggregation_by_metrics_threshold_rule': {
|
||||||
'metrics': ['b3d9d8ab-05e8-439f-89ad-5e978dd2a5eb',
|
'metrics': ['b3d9d8ab-05e8-439f-89ad-5e978dd2a5eb',
|
||||||
'009d4faf-c275-46f0-8f2d-670b15bac2b0'],
|
'009d4faf-c275-46f0-8f2d-670b15bac2b0'],
|
||||||
'comparison_operator': 'le',
|
'comparison_operator': 'le',
|
||||||
@ -2497,18 +2524,18 @@ class TestAlarms(v2.FunctionalTest,
|
|||||||
self._verify_alarm(json, alarms[0])
|
self._verify_alarm(json, alarms[0])
|
||||||
|
|
||||||
@mock.patch('ceilometer.keystone_client.get_client')
|
@mock.patch('ceilometer.keystone_client.get_client')
|
||||||
def test_post_gnocchi_resources_alarm_project_constraint(self, __):
|
def test_post_gnocchi_aggregation_alarm_project_constraint(self, __):
|
||||||
json = {
|
json = {
|
||||||
'enabled': False,
|
'enabled': False,
|
||||||
'name': 'name_post',
|
'name': 'project_constraint',
|
||||||
'state': 'ok',
|
'state': 'ok',
|
||||||
'type': 'gnocchi_resources_threshold',
|
'type': 'gnocchi_aggregation_by_resources_threshold',
|
||||||
'severity': 'critical',
|
'severity': 'critical',
|
||||||
'ok_actions': ['http://something/ok'],
|
'ok_actions': ['http://something/ok'],
|
||||||
'alarm_actions': ['http://something/alarm'],
|
'alarm_actions': ['http://something/alarm'],
|
||||||
'insufficient_data_actions': ['http://something/no'],
|
'insufficient_data_actions': ['http://something/no'],
|
||||||
'repeat_actions': True,
|
'repeat_actions': True,
|
||||||
'gnocchi_resources_threshold_rule': {
|
'gnocchi_aggregation_by_resources_threshold_rule': {
|
||||||
'metric': 'ameter',
|
'metric': 'ameter',
|
||||||
'comparison_operator': 'le',
|
'comparison_operator': 'le',
|
||||||
'aggregation_method': 'count',
|
'aggregation_method': 'count',
|
||||||
@ -2516,7 +2543,7 @@ class TestAlarms(v2.FunctionalTest,
|
|||||||
'evaluation_periods': 3,
|
'evaluation_periods': 3,
|
||||||
'granularity': 180,
|
'granularity': 180,
|
||||||
'resource_type': 'instance',
|
'resource_type': 'instance',
|
||||||
'resource_constraint': u'server_group=as',
|
'query': '{"=": {"server_group": "my_autoscaling_group"}}',
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2524,15 +2551,34 @@ class TestAlarms(v2.FunctionalTest,
|
|||||||
text=jsonutils.dumps(
|
text=jsonutils.dumps(
|
||||||
{'aggregation_methods': ['count']}))
|
{'aggregation_methods': ['count']}))
|
||||||
resource_result = mock.Mock(status_code=200, text="blob")
|
resource_result = mock.Mock(status_code=200, text="blob")
|
||||||
|
query_check_result = mock.Mock(status_code=200, text="blob")
|
||||||
|
|
||||||
|
expected_query = ('{"and": [{"=": {"created_by_project_id": "%s"}}, '
|
||||||
|
'{"=": {"server_group": "my_autoscaling_group"}}]}' %
|
||||||
|
self.auth_headers['X-Project-Id'])
|
||||||
|
|
||||||
with mock.patch('requests.get',
|
with mock.patch('requests.get',
|
||||||
side_effect=[cap_result, resource_result]):
|
side_effect=[cap_result, resource_result]):
|
||||||
self.post_json('/alarms', params=json, headers=self.auth_headers)
|
with mock.patch('requests.post',
|
||||||
|
side_effect=[query_check_result]) as fake_post:
|
||||||
|
|
||||||
|
self.post_json('/alarms', params=json,
|
||||||
|
headers=self.auth_headers)
|
||||||
|
|
||||||
|
self.assertEqual([mock.call(
|
||||||
|
url=('http://localhost:8041/v1/aggregation/'
|
||||||
|
'resource/instance/metric/ameter/measures'),
|
||||||
|
headers={'Content-Type': 'application/json',
|
||||||
|
'X-Auth-Token': mock.ANY},
|
||||||
|
params={'aggregation': 'count'},
|
||||||
|
data=expected_query)],
|
||||||
|
fake_post.mock_calls),
|
||||||
|
|
||||||
alarms = list(self.alarm_conn.get_alarms(enabled=False))
|
alarms = list(self.alarm_conn.get_alarms(enabled=False))
|
||||||
self.assertEqual(1, len(alarms))
|
self.assertEqual(1, len(alarms))
|
||||||
|
|
||||||
json['gnocchi_resources_threshold_rule']['resource_constraint'] += (
|
json['gnocchi_aggregation_by_resources_threshold_rule']['query'] = (
|
||||||
u'\u2227project_id=%s' % self.auth_headers['X-Project-Id'])
|
expected_query)
|
||||||
self._verify_alarm(json, alarms[0])
|
self._verify_alarm(json, alarms[0])
|
||||||
|
|
||||||
|
|
||||||
|
@ -100,10 +100,13 @@ Alarms
|
|||||||
.. autotype:: ceilometer.api.controllers.v2.alarm_rules.combination.AlarmCombinationRule
|
.. autotype:: ceilometer.api.controllers.v2.alarm_rules.combination.AlarmCombinationRule
|
||||||
:members:
|
:members:
|
||||||
|
|
||||||
.. autotype:: ceilometer.api.controllers.v2.alarm_rules.gnocchi.AlarmGnocchiMetricOfResourcesThresholdRule
|
.. autotype:: ceilometer.api.controllers.v2.alarm_rules.gnocchi.MetricOfResourceRule
|
||||||
:members:
|
:members:
|
||||||
|
|
||||||
.. autotype:: ceilometer.api.controllers.v2.alarm_rules.gnocchi.AlarmGnocchiMetricsThresholdRule
|
.. autotype:: ceilometer.api.controllers.v2.alarm_rules.gnocchi.AggregationMetricByResourcesLookupRule
|
||||||
|
:members:
|
||||||
|
|
||||||
|
.. autotype:: ceilometer.api.controllers.v2.alarm_rules.gnocchi.AggregationMetricsByIdLookupRule
|
||||||
:members:
|
:members:
|
||||||
|
|
||||||
.. autotype:: ceilometer.api.controllers.v2.alarms.AlarmTimeConstraint
|
.. autotype:: ceilometer.api.controllers.v2.alarms.AlarmTimeConstraint
|
||||||
|
@ -298,14 +298,16 @@ ceilometer.event.publisher =
|
|||||||
ceilometer.alarm.rule =
|
ceilometer.alarm.rule =
|
||||||
threshold = ceilometer.api.controllers.v2.alarm_rules.threshold:AlarmThresholdRule
|
threshold = ceilometer.api.controllers.v2.alarm_rules.threshold:AlarmThresholdRule
|
||||||
combination = ceilometer.api.controllers.v2.alarm_rules.combination:AlarmCombinationRule
|
combination = ceilometer.api.controllers.v2.alarm_rules.combination:AlarmCombinationRule
|
||||||
gnocchi_resources_threshold = ceilometer.api.controllers.v2.alarm_rules.gnocchi:AlarmGnocchiMetricOfResourcesThresholdRule
|
gnocchi_resources_threshold = ceilometer.api.controllers.v2.alarm_rules.gnocchi:MetricOfResourceRule
|
||||||
gnocchi_metrics_threshold = ceilometer.api.controllers.v2.alarm_rules.gnocchi:AlarmGnocchiMetricsThresholdRule
|
gnocchi_aggregation_by_metrics_threshold = ceilometer.api.controllers.v2.alarm_rules.gnocchi:AggregationMetricsByIdLookupRule
|
||||||
|
gnocchi_aggregation_by_resources_threshold = ceilometer.api.controllers.v2.alarm_rules.gnocchi:AggregationMetricByResourcesLookupRule
|
||||||
|
|
||||||
ceilometer.alarm.evaluator =
|
ceilometer.alarm.evaluator =
|
||||||
threshold = ceilometer.alarm.evaluator.threshold:ThresholdEvaluator
|
threshold = ceilometer.alarm.evaluator.threshold:ThresholdEvaluator
|
||||||
combination = ceilometer.alarm.evaluator.combination:CombinationEvaluator
|
combination = ceilometer.alarm.evaluator.combination:CombinationEvaluator
|
||||||
gnocchi_resources_threshold = ceilometer.alarm.evaluator.gnocchi:GnocchiThresholdEvaluator
|
gnocchi_resources_threshold = ceilometer.alarm.evaluator.gnocchi:GnocchiThresholdEvaluator
|
||||||
gnocchi_metrics_threshold = ceilometer.alarm.evaluator.gnocchi:GnocchiThresholdEvaluator
|
gnocchi_aggregation_by_metrics_threshold = ceilometer.alarm.evaluator.gnocchi:GnocchiThresholdEvaluator
|
||||||
|
gnocchi_aggregation_by_resources_threshold = ceilometer.alarm.evaluator.gnocchi:GnocchiThresholdEvaluator
|
||||||
|
|
||||||
ceilometer.alarm.evaluator_service =
|
ceilometer.alarm.evaluator_service =
|
||||||
default = ceilometer.alarm.service:AlarmEvaluationService
|
default = ceilometer.alarm.service:AlarmEvaluationService
|
||||||
|
Loading…
x
Reference in New Issue
Block a user