gnocchi: use gnocchiclient instead of requests
Depends-On: Ifc19e485267b321cfbd42e7ae977462826820526 Change-Id: I60cf8a940134db7a2c298861ab26fe202d2181f3
This commit is contained in:
parent
46da8efef0
commit
fa3a982a27
@ -13,9 +13,10 @@
|
|||||||
# License for the specific language governing permissions and limitations
|
# License for the specific language governing permissions and limitations
|
||||||
# under the License.
|
# under the License.
|
||||||
|
|
||||||
|
from gnocchiclient import client
|
||||||
|
from gnocchiclient import exceptions
|
||||||
from oslo_serialization import jsonutils
|
from oslo_serialization import jsonutils
|
||||||
import pecan
|
import pecan
|
||||||
import requests
|
|
||||||
import wsme
|
import wsme
|
||||||
from wsme import types as wtypes
|
from wsme import types as wtypes
|
||||||
|
|
||||||
@ -60,19 +61,20 @@ class AlarmGnocchiThresholdRule(base.AlarmRule):
|
|||||||
# @cachetools.ttl_cache(maxsize=1, ttl=600)
|
# @cachetools.ttl_cache(maxsize=1, ttl=600)
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _get_aggregation_methods():
|
def _get_aggregation_methods():
|
||||||
ks_client = keystone_client.get_client(pecan.request.cfg)
|
conf = pecan.request.cfg
|
||||||
gnocchi_url = pecan.request.cfg.gnocchi_url
|
gnocchi_client = client.Client(
|
||||||
headers = {'Content-Type': "application/json",
|
'1', keystone_client.get_session(conf),
|
||||||
'X-Auth-Token': keystone_client.get_auth_token(ks_client)}
|
interface=conf.service_credentials.interface,
|
||||||
try:
|
region_name=conf.service_credentials.region_name,
|
||||||
r = requests.get("%s/v1/capabilities" % gnocchi_url,
|
endpoint_override=conf.gnocchi_url)
|
||||||
headers=headers)
|
|
||||||
except requests.ConnectionError as e:
|
|
||||||
raise GnocchiUnavailable(e)
|
|
||||||
if r.status_code // 200 != 1:
|
|
||||||
raise GnocchiUnavailable(r.text)
|
|
||||||
|
|
||||||
return jsonutils.loads(r.text).get('aggregation_methods', [])
|
try:
|
||||||
|
return gnocchi_client.capabilities.list().get(
|
||||||
|
'aggregation_methods', [])
|
||||||
|
except exceptions.ClientException as e:
|
||||||
|
raise base.ClientSideError(e.message, status_code=e.code)
|
||||||
|
except Exception as e:
|
||||||
|
raise GnocchiUnavailable(e)
|
||||||
|
|
||||||
|
|
||||||
class MetricOfResourceRule(AlarmGnocchiThresholdRule):
|
class MetricOfResourceRule(AlarmGnocchiThresholdRule):
|
||||||
@ -99,23 +101,21 @@ class MetricOfResourceRule(AlarmGnocchiThresholdRule):
|
|||||||
super(MetricOfResourceRule,
|
super(MetricOfResourceRule,
|
||||||
cls).validate_alarm(alarm)
|
cls).validate_alarm(alarm)
|
||||||
|
|
||||||
|
conf = pecan.request.cfg
|
||||||
|
gnocchi_client = client.Client(
|
||||||
|
'1', keystone_client.get_session(conf),
|
||||||
|
interface=conf.service_credentials.interface,
|
||||||
|
region_name=conf.service_credentials.region_name,
|
||||||
|
endpoint_override=conf.gnocchi_url)
|
||||||
|
|
||||||
rule = alarm.gnocchi_resources_threshold_rule
|
rule = alarm.gnocchi_resources_threshold_rule
|
||||||
ks_client = keystone_client.get_client(pecan.request.cfg)
|
|
||||||
gnocchi_url = pecan.request.cfg.gnocchi_url
|
|
||||||
headers = {'Content-Type': "application/json",
|
|
||||||
'X-Auth-Token': keystone_client.get_auth_token(ks_client)}
|
|
||||||
try:
|
try:
|
||||||
r = requests.get("%s/v1/resource/%s/%s" % (
|
gnocchi_client.resource.get(rule.resource_type,
|
||||||
gnocchi_url, rule.resource_type,
|
rule.resource_id)
|
||||||
rule.resource_id),
|
except exceptions.ClientException as e:
|
||||||
headers=headers)
|
raise base.ClientSideError(e.message, status_code=e.code)
|
||||||
except requests.ConnectionError as e:
|
except Exception as e:
|
||||||
raise GnocchiUnavailable(e)
|
raise GnocchiUnavailable(e)
|
||||||
if r.status_code == 404:
|
|
||||||
raise base.EntityNotFound('gnocchi resource',
|
|
||||||
rule.resource_id)
|
|
||||||
elif r.status_code // 200 != 1:
|
|
||||||
raise base.ClientSideError(r.content, status_code=r.status_code)
|
|
||||||
|
|
||||||
|
|
||||||
class AggregationMetricByResourcesLookupRule(AlarmGnocchiThresholdRule):
|
class AggregationMetricByResourcesLookupRule(AlarmGnocchiThresholdRule):
|
||||||
@ -156,31 +156,28 @@ class AggregationMetricByResourcesLookupRule(AlarmGnocchiThresholdRule):
|
|||||||
# Scope the alarm to the project id if needed
|
# Scope the alarm to the project id if needed
|
||||||
auth_project = v2_utils.get_auth_project(alarm.project_id)
|
auth_project = v2_utils.get_auth_project(alarm.project_id)
|
||||||
if auth_project:
|
if auth_project:
|
||||||
rule.query = jsonutils.dumps({
|
query = {"and": [{"=": {"created_by_project_id": auth_project}},
|
||||||
"and": [{"=": {"created_by_project_id": auth_project}},
|
query]}
|
||||||
query]})
|
rule.query = jsonutils.dumps(query)
|
||||||
|
|
||||||
# Delegate the query validation to gnocchi
|
conf = pecan.request.cfg
|
||||||
ks_client = keystone_client.get_client(pecan.request.cfg)
|
gnocchi_client = client.Client(
|
||||||
request = {
|
'1', keystone_client.get_session(conf),
|
||||||
'url': "%s/v1/aggregation/resource/%s/metric/%s" % (
|
interface=conf.service_credentials.interface,
|
||||||
pecan.request.cfg.gnocchi_url,
|
region_name=conf.service_credentials.region_name,
|
||||||
rule.resource_type,
|
endpoint_override=conf.gnocchi_url)
|
||||||
rule.metric),
|
|
||||||
'headers': {'Content-Type': "application/json",
|
|
||||||
'X-Auth-Token': keystone_client.get_auth_token(
|
|
||||||
ks_client)},
|
|
||||||
'params': {'aggregation': rule.aggregation_method,
|
|
||||||
'needed_overlap': 0},
|
|
||||||
'data': rule.query,
|
|
||||||
}
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
r = requests.post(**request)
|
gnocchi_client.metric.aggregation(
|
||||||
except requests.ConnectionError as e:
|
metrics=rule.metric,
|
||||||
|
query=query,
|
||||||
|
aggregation=rule.aggregation_method,
|
||||||
|
needed_overlap=0,
|
||||||
|
resource_type=rule.resource_type)
|
||||||
|
except exceptions.ClientException as e:
|
||||||
|
raise base.ClientSideError(e.message, status_code=e.code)
|
||||||
|
except Exception as e:
|
||||||
raise GnocchiUnavailable(e)
|
raise GnocchiUnavailable(e)
|
||||||
if r.status_code // 200 != 1:
|
|
||||||
raise base.ClientSideError(r.content, status_code=r.status_code)
|
|
||||||
|
|
||||||
|
|
||||||
class AggregationMetricsByIdLookupRule(AlarmGnocchiThresholdRule):
|
class AggregationMetricsByIdLookupRule(AlarmGnocchiThresholdRule):
|
||||||
|
@ -13,10 +13,10 @@
|
|||||||
# License for the specific language governing permissions and limitations
|
# License for the specific language governing permissions and limitations
|
||||||
# under the License.
|
# under the License.
|
||||||
|
|
||||||
|
from gnocchiclient import client
|
||||||
from oslo_config import cfg
|
from oslo_config import cfg
|
||||||
from oslo_log import log
|
from oslo_log import log
|
||||||
from oslo_serialization import jsonutils
|
from oslo_serialization import jsonutils
|
||||||
import requests
|
|
||||||
|
|
||||||
from aodh.evaluator import threshold
|
from aodh.evaluator import threshold
|
||||||
from aodh.i18n import _
|
from aodh.i18n import _
|
||||||
@ -27,8 +27,8 @@ LOG = log.getLogger(__name__)
|
|||||||
OPTS = [
|
OPTS = [
|
||||||
cfg.StrOpt('gnocchi_url',
|
cfg.StrOpt('gnocchi_url',
|
||||||
deprecated_group="alarm",
|
deprecated_group="alarm",
|
||||||
default="http://localhost:8041",
|
deprecated_for_removal=True,
|
||||||
help='URL to Gnocchi.'),
|
help='URL to Gnocchi. default: autodetection'),
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
@ -36,61 +36,46 @@ class GnocchiThresholdEvaluator(threshold.ThresholdEvaluator):
|
|||||||
|
|
||||||
def __init__(self, conf):
|
def __init__(self, conf):
|
||||||
super(threshold.ThresholdEvaluator, self).__init__(conf)
|
super(threshold.ThresholdEvaluator, self).__init__(conf)
|
||||||
self.gnocchi_url = conf.gnocchi_url
|
self._gnocchi_client = client.Client(
|
||||||
|
'1', keystone_client.get_session(conf),
|
||||||
def _get_headers(self, content_type="application/json"):
|
interface=conf.service_credentials.interface,
|
||||||
return {
|
region_name=conf.service_credentials.region_name,
|
||||||
'Content-Type': content_type,
|
endpoint_override=conf.gnocchi_url)
|
||||||
'X-Auth-Token': keystone_client.get_auth_token(self.ks_client),
|
|
||||||
}
|
|
||||||
|
|
||||||
def _statistics(self, alarm, start, end):
|
def _statistics(self, alarm, start, end):
|
||||||
"""Retrieve statistics over the current window."""
|
"""Retrieve statistics over the current window."""
|
||||||
method = 'get'
|
|
||||||
req = {
|
|
||||||
'url': self.gnocchi_url + "/v1",
|
|
||||||
'headers': self._get_headers(),
|
|
||||||
'params': {
|
|
||||||
'aggregation': alarm.rule['aggregation_method'],
|
|
||||||
'start': start,
|
|
||||||
'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']
|
|
||||||
# FIXME(sileht): In case of a heat autoscaling stack decide to
|
|
||||||
# delete an instance, the gnocchi metrics associated to this
|
|
||||||
# instance will be no more updated and when the alarm will ask
|
|
||||||
# for the aggregation, gnocchi will raise a 'No overlap' exception.
|
|
||||||
# So temporary set 'needed_overlap' to 0 to disable the
|
|
||||||
# gnocchi checks about missing points. For more detail see:
|
|
||||||
# https://bugs.launchpad.net/gnocchi/+bug/1479429
|
|
||||||
req['params']['needed_overlap'] = 0
|
|
||||||
|
|
||||||
elif alarm.type == 'gnocchi_aggregation_by_metrics_threshold':
|
|
||||||
req['url'] += "/aggregation/metric"
|
|
||||||
req['params']['metric'] = alarm.rule['metrics']
|
|
||||||
|
|
||||||
elif alarm.type == 'gnocchi_resources_threshold':
|
|
||||||
req['url'] += "/resource/%s/%s/metric/%s/measures" % (
|
|
||||||
alarm.rule['resource_type'],
|
|
||||||
alarm.rule['resource_id'], alarm.rule['metric'])
|
|
||||||
|
|
||||||
LOG.debug('stats query %s', req['url'])
|
|
||||||
try:
|
try:
|
||||||
r = getattr(requests, method)(**req)
|
if alarm.type == 'gnocchi_aggregation_by_resources_threshold':
|
||||||
|
# FIXME(sileht): In case of a heat autoscaling stack decide to
|
||||||
|
# delete an instance, the gnocchi metrics associated to this
|
||||||
|
# instance will be no more updated and when the alarm will ask
|
||||||
|
# for the aggregation, gnocchi will raise a 'No overlap'
|
||||||
|
# exception.
|
||||||
|
# So temporary set 'needed_overlap' to 0 to disable the
|
||||||
|
# gnocchi checks about missing points. For more detail see:
|
||||||
|
# https://bugs.launchpad.net/gnocchi/+bug/1479429
|
||||||
|
return self._gnocchi_client.metric.aggregation(
|
||||||
|
metrics=alarm.rule['metric'],
|
||||||
|
query=jsonutils.loads(alarm.rule['query']),
|
||||||
|
resource_type=alarm.rule["resource_type"],
|
||||||
|
start=start, stop=end,
|
||||||
|
aggregation=alarm.rule['aggregation_method'],
|
||||||
|
needed_overlap=0,
|
||||||
|
)
|
||||||
|
elif alarm.type == 'gnocchi_aggregation_by_metrics_threshold':
|
||||||
|
return self._gnocchi_client.metric.aggregation(
|
||||||
|
metrics=alarm.rule['metrics'],
|
||||||
|
start=start, stop=end,
|
||||||
|
aggregation=alarm.rule['aggregation_method'])
|
||||||
|
elif alarm.type == 'gnocchi_resources_threshold':
|
||||||
|
return self._gnocchi_client.metric.get_measures(
|
||||||
|
metric=alarm.rule['metric'],
|
||||||
|
start=start, stop=end,
|
||||||
|
resource_id=alarm.rule['resource_id'],
|
||||||
|
aggregation=alarm.rule['aggregation_method'])
|
||||||
except Exception:
|
except Exception:
|
||||||
LOG.exception(_('alarm stats retrieval failed'))
|
LOG.exception(_('alarm stats retrieval failed'))
|
||||||
return []
|
return []
|
||||||
if int(r.status_code / 100) != 2:
|
|
||||||
LOG.exception(_('alarm stats retrieval failed: %s') % r.text)
|
|
||||||
return []
|
|
||||||
else:
|
|
||||||
return jsonutils.loads(r.text)
|
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _sanitize(alarm, statistics):
|
def _sanitize(alarm, statistics):
|
||||||
@ -100,9 +85,10 @@ class GnocchiThresholdEvaluator(threshold.ThresholdEvaluator):
|
|||||||
# we could potentially do a mean-of-means (or max-of-maxes or whatever,
|
# we could potentially do a mean-of-means (or max-of-maxes or whatever,
|
||||||
# but not a stddev-of-stddevs).
|
# but not a stddev-of-stddevs).
|
||||||
# TODO(sileht): support alarm['exclude_outliers']
|
# TODO(sileht): support alarm['exclude_outliers']
|
||||||
LOG.debug('sanitize stats %s', statistics)
|
LOG.error('sanitize (%s) stats %s', alarm.rule['granularity'],
|
||||||
|
statistics)
|
||||||
statistics = [stats[2] for stats in statistics
|
statistics = [stats[2] for stats in statistics
|
||||||
if stats[1] == alarm.rule['granularity']]
|
if stats[1] == alarm.rule['granularity']]
|
||||||
statistics = statistics[-alarm.rule['evaluation_periods']:]
|
statistics = statistics[-alarm.rule['evaluation_periods']:]
|
||||||
LOG.debug('pruned statistics to %d', len(statistics))
|
LOG.error('pruned statistics to %d', len(statistics))
|
||||||
return statistics
|
return statistics
|
||||||
|
@ -18,13 +18,12 @@ import datetime
|
|||||||
import os
|
import os
|
||||||
import uuid
|
import uuid
|
||||||
|
|
||||||
|
from gnocchiclient import exceptions
|
||||||
import mock
|
import mock
|
||||||
import oslo_messaging.conffixture
|
import oslo_messaging.conffixture
|
||||||
from oslo_serialization import jsonutils
|
from oslo_serialization import jsonutils
|
||||||
import requests
|
|
||||||
import six
|
import six
|
||||||
from six import moves
|
from six import moves
|
||||||
import six.moves.urllib.parse as urlparse
|
|
||||||
|
|
||||||
from aodh import messaging
|
from aodh import messaging
|
||||||
from aodh.storage import models
|
from aodh.storage import models
|
||||||
@ -2846,8 +2845,7 @@ class TestAlarmsRuleGnocchi(TestAlarmsBase):
|
|||||||
for r in data
|
for r in data
|
||||||
if 'gnocchi_resources_threshold_rule' in r))
|
if 'gnocchi_resources_threshold_rule' in r))
|
||||||
|
|
||||||
@mock.patch('aodh.keystone_client.get_client')
|
def test_post_gnocchi_resources_alarm(self):
|
||||||
def test_post_gnocchi_resources_alarm(self, __):
|
|
||||||
json = {
|
json = {
|
||||||
'enabled': False,
|
'enabled': False,
|
||||||
'name': 'name_post',
|
'name': 'name_post',
|
||||||
@ -2870,53 +2868,44 @@ class TestAlarmsRuleGnocchi(TestAlarmsBase):
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
with mock.patch('requests.get',
|
with mock.patch('aodh.api.controllers.v2.alarm_rules.'
|
||||||
side_effect=requests.ConnectionError()):
|
'gnocchi.client') as clientlib:
|
||||||
|
c = clientlib.Client.return_value
|
||||||
|
c.capabilities.list.side_effect = Exception("boom!")
|
||||||
resp = self.post_json('/alarms', params=json,
|
resp = self.post_json('/alarms', params=json,
|
||||||
headers=self.auth_headers,
|
headers=self.auth_headers,
|
||||||
expect_errors=True)
|
expect_errors=True)
|
||||||
self.assertEqual(503, resp.status_code, resp.body)
|
self.assertEqual(503, resp.status_code, resp.body)
|
||||||
|
|
||||||
with mock.patch('requests.get',
|
with mock.patch('aodh.api.controllers.v2.alarm_rules.'
|
||||||
return_value=mock.Mock(status_code=500,
|
'gnocchi.client') as clientlib:
|
||||||
body="my_custom_error",
|
c = clientlib.Client.return_value
|
||||||
text="my_custom_error")):
|
c.capabilities.list.side_effect = (
|
||||||
|
exceptions.ClientException(500, "my_custom_error"))
|
||||||
resp = self.post_json('/alarms', params=json,
|
resp = self.post_json('/alarms', params=json,
|
||||||
headers=self.auth_headers,
|
headers=self.auth_headers,
|
||||||
expect_errors=True)
|
expect_errors=True)
|
||||||
self.assertEqual(503, resp.status_code, resp.body)
|
self.assertEqual(500, resp.status_code, resp.body)
|
||||||
self.assertIn('my_custom_error',
|
self.assertIn('my_custom_error',
|
||||||
resp.json['error_message']['faultstring'])
|
resp.json['error_message']['faultstring'])
|
||||||
|
|
||||||
cap_result = mock.Mock(status_code=201,
|
with mock.patch('aodh.api.controllers.v2.alarm_rules.'
|
||||||
text=jsonutils.dumps(
|
'gnocchi.client') as clientlib:
|
||||||
{'aggregation_methods': ['count']}))
|
c = clientlib.Client.return_value
|
||||||
resource_result = mock.Mock(status_code=200, text="blob")
|
c.capabilities.list.return_value = {
|
||||||
with mock.patch('requests.get', side_effect=[cap_result,
|
'aggregation_methods': ['count']}
|
||||||
resource_result]
|
|
||||||
) as gnocchi_get:
|
|
||||||
self.post_json('/alarms', params=json, headers=self.auth_headers)
|
self.post_json('/alarms', params=json, headers=self.auth_headers)
|
||||||
|
expected = [mock.call.capabilities.list(),
|
||||||
gnocchi_url = self.CONF.gnocchi_url
|
mock.call.resource.get(
|
||||||
capabilities_url = urlparse.urljoin(gnocchi_url,
|
"instance",
|
||||||
'/v1/capabilities')
|
"209ef69c-c10c-4efb-90ff-46f4b2d90d2e")]
|
||||||
resource_url = urlparse.urljoin(
|
self.assertEqual(expected, c.mock_calls)
|
||||||
gnocchi_url,
|
|
||||||
'/v1/resource/instance/209ef69c-c10c-4efb-90ff-46f4b2d90d2e'
|
|
||||||
)
|
|
||||||
|
|
||||||
expected = [mock.call(capabilities_url,
|
|
||||||
headers=mock.ANY),
|
|
||||||
mock.call(resource_url,
|
|
||||||
headers=mock.ANY)]
|
|
||||||
self.assertEqual(expected, gnocchi_get.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))
|
||||||
self._verify_alarm(json, alarms[0])
|
self._verify_alarm(json, alarms[0])
|
||||||
|
|
||||||
@mock.patch('aodh.keystone_client.get_client')
|
def test_post_gnocchi_metrics_alarm(self):
|
||||||
def test_post_gnocchi_metrics_alarm(self, __):
|
|
||||||
json = {
|
json = {
|
||||||
'enabled': False,
|
'enabled': False,
|
||||||
'name': 'name_post',
|
'name': 'name_post',
|
||||||
@ -2938,19 +2927,19 @@ class TestAlarmsRuleGnocchi(TestAlarmsBase):
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
cap_result = mock.Mock(status_code=200,
|
with mock.patch('aodh.api.controllers.v2.alarm_rules.'
|
||||||
text=jsonutils.dumps(
|
'gnocchi.client') as clientlib:
|
||||||
{'aggregation_methods': ['count']}))
|
c = clientlib.Client.return_value
|
||||||
with mock.patch('requests.get', return_value=cap_result):
|
c.capabilities.list.return_value = {
|
||||||
|
'aggregation_methods': ['count']}
|
||||||
|
|
||||||
self.post_json('/alarms', params=json, headers=self.auth_headers)
|
self.post_json('/alarms', params=json, headers=self.auth_headers)
|
||||||
|
|
||||||
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))
|
||||||
self._verify_alarm(json, alarms[0])
|
self._verify_alarm(json, alarms[0])
|
||||||
|
|
||||||
@mock.patch('aodh.keystone_client.get_client')
|
def test_post_gnocchi_aggregation_alarm_project_constraint(self):
|
||||||
def test_post_gnocchi_aggregation_alarm_project_constraint(self, __):
|
|
||||||
self.CONF.set_override('gnocchi_url', 'http://localhost:8041')
|
|
||||||
json = {
|
json = {
|
||||||
'enabled': False,
|
'enabled': False,
|
||||||
'name': 'project_constraint',
|
'name': 'project_constraint',
|
||||||
@ -2973,39 +2962,31 @@ class TestAlarmsRuleGnocchi(TestAlarmsBase):
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
cap_result = mock.Mock(status_code=201,
|
expected_query = {"and": [{"=": {"created_by_project_id":
|
||||||
text=jsonutils.dumps(
|
self.auth_headers['X-Project-Id']}},
|
||||||
{'aggregation_methods': ['count']}))
|
{"=": {"server_group":
|
||||||
resource_result = mock.Mock(status_code=200, text="blob")
|
"my_autoscaling_group"}}]}
|
||||||
query_check_result = mock.Mock(status_code=200, text="blob")
|
|
||||||
|
|
||||||
expected_query = ('{"and": [{"=": {"created_by_project_id": "%s"}}, '
|
with mock.patch('aodh.api.controllers.v2.alarm_rules.'
|
||||||
'{"=": {"server_group": "my_autoscaling_group"}}]}' %
|
'gnocchi.client') as clientlib:
|
||||||
self.auth_headers['X-Project-Id'])
|
c = clientlib.Client.return_value
|
||||||
|
c.capabilities.list.return_value = {
|
||||||
|
'aggregation_methods': ['count']}
|
||||||
|
self.post_json('/alarms', params=json, headers=self.auth_headers)
|
||||||
|
|
||||||
with mock.patch('requests.get',
|
self.assertEqual([mock.call(
|
||||||
side_effect=[cap_result, resource_result]):
|
aggregation='count',
|
||||||
with mock.patch('requests.post',
|
metrics='ameter',
|
||||||
side_effect=[query_check_result]) as fake_post:
|
needed_overlap=0,
|
||||||
|
query=expected_query,
|
||||||
self.post_json('/alarms', params=json,
|
resource_type="instance")],
|
||||||
headers=self.auth_headers)
|
c.metric.aggregation.mock_calls),
|
||||||
|
|
||||||
self.assertEqual([mock.call(
|
|
||||||
url=('http://localhost:8041/v1/aggregation/'
|
|
||||||
'resource/instance/metric/ameter'),
|
|
||||||
headers={'Content-Type': 'application/json',
|
|
||||||
'X-Auth-Token': mock.ANY},
|
|
||||||
params={'aggregation': 'count',
|
|
||||||
'needed_overlap': 0},
|
|
||||||
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_aggregation_by_resources_threshold_rule']['query'] = (
|
json['gnocchi_aggregation_by_resources_threshold_rule']['query'] = (
|
||||||
expected_query)
|
jsonutils.dumps(expected_query))
|
||||||
self._verify_alarm(json, alarms[0])
|
self._verify_alarm(json, alarms[0])
|
||||||
|
|
||||||
|
|
||||||
|
@ -17,8 +17,8 @@ import datetime
|
|||||||
import unittest
|
import unittest
|
||||||
import uuid
|
import uuid
|
||||||
|
|
||||||
|
from gnocchiclient import exceptions
|
||||||
import mock
|
import mock
|
||||||
from oslo_serialization import jsonutils
|
|
||||||
from oslo_utils import timeutils
|
from oslo_utils import timeutils
|
||||||
from oslotest import mockpatch
|
from oslotest import mockpatch
|
||||||
import pytz
|
import pytz
|
||||||
@ -31,25 +31,15 @@ from aodh.tests import constants
|
|||||||
from aodh.tests.unit.evaluator import base
|
from aodh.tests.unit.evaluator import base
|
||||||
|
|
||||||
|
|
||||||
class FakeResponse(object):
|
|
||||||
def __init__(self, code, data):
|
|
||||||
if code == 200:
|
|
||||||
self.values = [d[2] for d in data]
|
|
||||||
else:
|
|
||||||
self.values = []
|
|
||||||
self.text = jsonutils.dumps(data)
|
|
||||||
self.status_code = code
|
|
||||||
|
|
||||||
|
|
||||||
class TestGnocchiThresholdEvaluate(base.TestEvaluatorBase):
|
class TestGnocchiThresholdEvaluate(base.TestEvaluatorBase):
|
||||||
EVALUATOR = gnocchi.GnocchiThresholdEvaluator
|
EVALUATOR = gnocchi.GnocchiThresholdEvaluator
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
|
self.client = self.useFixture(mockpatch.Patch(
|
||||||
|
'aodh.evaluator.gnocchi.client'
|
||||||
|
)).mock.Client.return_value
|
||||||
super(TestGnocchiThresholdEvaluate, self).setUp()
|
super(TestGnocchiThresholdEvaluate, self).setUp()
|
||||||
|
|
||||||
self.requests = self.useFixture(mockpatch.Patch(
|
|
||||||
'aodh.evaluator.gnocchi.requests')).mock
|
|
||||||
|
|
||||||
def prepare_alarms(self):
|
def prepare_alarms(self):
|
||||||
self.alarms = [
|
self.alarms = [
|
||||||
models.Alarm(name='instance_running_hot',
|
models.Alarm(name='instance_running_hot',
|
||||||
@ -133,9 +123,8 @@ class TestGnocchiThresholdEvaluate(base.TestEvaluatorBase):
|
|||||||
@staticmethod
|
@staticmethod
|
||||||
def _get_stats(granularity, values):
|
def _get_stats(granularity, values):
|
||||||
now = timeutils.utcnow_ts()
|
now = timeutils.utcnow_ts()
|
||||||
return FakeResponse(
|
return [[six.text_type(now - len(values) * granularity),
|
||||||
200, [[six.text_type(now - len(values) * granularity),
|
granularity, value] for value in values]
|
||||||
granularity, value] for value in values])
|
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _reason_data(disposition, count, most_recent):
|
def _reason_data(disposition, count, most_recent):
|
||||||
@ -153,11 +142,13 @@ class TestGnocchiThresholdEvaluate(base.TestEvaluatorBase):
|
|||||||
for v in moves.xrange(4)])
|
for v in moves.xrange(4)])
|
||||||
avgs2 = self._get_stats(50, [self.alarms[2].rule['threshold'] - v
|
avgs2 = self._get_stats(50, [self.alarms[2].rule['threshold'] - v
|
||||||
for v in moves.xrange(6)])
|
for v in moves.xrange(6)])
|
||||||
self.requests.get.side_effect = [Exception('boom'),
|
self.client.metric.get_measures.side_effect = [
|
||||||
FakeResponse(500, "error"),
|
exceptions.ClientException(501, "error2"),
|
||||||
means,
|
means]
|
||||||
maxs]
|
self.client.metric.aggregation.side_effect = [
|
||||||
self.requests.post.side_effect = [FakeResponse(500, "error"), avgs2]
|
Exception('boom'),
|
||||||
|
exceptions.ClientException(500, "error"),
|
||||||
|
maxs, 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()
|
||||||
@ -165,8 +156,8 @@ 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.client.metric.get_measures.return_value = []
|
||||||
self.requests.post.return_value = FakeResponse(200, [])
|
self.client.metric.aggregation.return_value = []
|
||||||
self._evaluate_all_alarms()
|
self._evaluate_all_alarms()
|
||||||
self._assert_all_alarms('insufficient data')
|
self._assert_all_alarms('insufficient data')
|
||||||
expected = [mock.call(alarm) for alarm in self.alarms]
|
expected = [mock.call(alarm) for alarm in self.alarms]
|
||||||
@ -194,58 +185,46 @@ class TestGnocchiThresholdEvaluate(base.TestEvaluatorBase):
|
|||||||
avgs2 = self._get_stats(50, [self.alarms[2].rule['threshold'] + v
|
avgs2 = self._get_stats(50, [self.alarms[2].rule['threshold'] + v
|
||||||
for v in moves.xrange(1, 7)])
|
for v in moves.xrange(1, 7)])
|
||||||
|
|
||||||
self.requests.get.side_effect = [avgs, maxs]
|
self.client.metric.get_measures.side_effect = [avgs]
|
||||||
self.requests.post.side_effect = [avgs2]
|
self.client.metric.aggregation.side_effect = [maxs, avgs2]
|
||||||
self._evaluate_all_alarms()
|
self._evaluate_all_alarms()
|
||||||
|
|
||||||
expected_headers = {'X-Auth-Token': mock.ANY,
|
|
||||||
'Content-Type': 'application/json'}
|
|
||||||
|
|
||||||
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"
|
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(url='http://localhost:8041/v1/resource/instance/'
|
mock.call.get_measures(aggregation='mean', metric='cpu_util',
|
||||||
'my_instance/metric/cpu_util/measures',
|
resource_id='my_instance',
|
||||||
params={'aggregation': 'mean',
|
start=start_alarm1, stop=end),
|
||||||
'start': start_alarm1, 'end': end},
|
mock.call.aggregation(aggregation='max',
|
||||||
headers=expected_headers),
|
metrics=[
|
||||||
mock.call(url='http://localhost:8041/v1/aggregation/metric',
|
'0bb1604d-1193-4c0a-b4b8-74b170e35e83',
|
||||||
params={'aggregation': 'max',
|
'9ddc209f-42f8-41e1-b8f1-8804f59c4053'],
|
||||||
'start': start_alarm2, 'end': end,
|
start=start_alarm2, stop=end),
|
||||||
'metric': [
|
mock.call.aggregation(aggregation='mean', metrics='cpu_util',
|
||||||
'0bb1604d-1193-4c0a-b4b8-74b170e35e83',
|
needed_overlap=0,
|
||||||
'9ddc209f-42f8-41e1-b8f1-8804f59c4053']},
|
query={"=": {"server_group":
|
||||||
headers=expected_headers)],
|
"my_autoscaling_group"}},
|
||||||
|
resource_type='instance',
|
||||||
self.requests.get.mock_calls)
|
start=start_alarm3, stop=end),
|
||||||
self.assertEqual([
|
], self.client.metric.mock_calls)
|
||||||
mock.call(url='http://localhost:8041/v1/aggregation/resource/'
|
|
||||||
'instance/metric/cpu_util',
|
|
||||||
params={'aggregation': 'mean',
|
|
||||||
'start': start_alarm3, 'end': end,
|
|
||||||
'needed_overlap': 0},
|
|
||||||
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) for alarm in self.alarms]
|
expected = [mock.call(alarm) for alarm in self.alarms]
|
||||||
update_calls = self.storage_conn.update_alarm.call_args_list
|
update_calls = self.storage_conn.update_alarm.call_args_list
|
||||||
self.assertEqual(expected, update_calls)
|
self.assertEqual(expected, update_calls)
|
||||||
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[-1][2],
|
||||||
'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[-1][2],
|
||||||
'Transition to alarm due to 6 samples outside'
|
'Transition to alarm due to 6 samples outside'
|
||||||
' threshold, most recent: %s' % avgs2.values[-1],
|
' threshold, most recent: %s' % avgs2[-1][2],
|
||||||
]
|
]
|
||||||
reason_datas = [self._reason_data('outside', 5, avgs.values[-1]),
|
reason_datas = [self._reason_data('outside', 5, avgs[-1][2]),
|
||||||
self._reason_data('outside', 4, maxs.values[-1]),
|
self._reason_data('outside', 4, maxs[-1][2]),
|
||||||
self._reason_data('outside', 6, avgs2.values[-1])]
|
self._reason_data('outside', 6, avgs2[-1][2])]
|
||||||
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)]
|
||||||
@ -259,22 +238,22 @@ class TestGnocchiThresholdEvaluate(base.TestEvaluatorBase):
|
|||||||
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
|
avgs2 = self._get_stats(50, [self.alarms[2].rule['threshold'] - v
|
||||||
for v in moves.xrange(6)])
|
for v in moves.xrange(6)])
|
||||||
self.requests.post.side_effect = [avgs2]
|
self.client.metric.get_measures.side_effect = [avgs]
|
||||||
self.requests.get.side_effect = [avgs, maxs]
|
self.client.metric.aggregation.side_effect = [maxs, avgs2]
|
||||||
self._evaluate_all_alarms()
|
self._evaluate_all_alarms()
|
||||||
self._assert_all_alarms('ok')
|
self._assert_all_alarms('ok')
|
||||||
expected = [mock.call(alarm) for alarm in self.alarms]
|
expected = [mock.call(alarm) for alarm in self.alarms]
|
||||||
update_calls = self.storage_conn.update_alarm.call_args_list
|
update_calls = self.storage_conn.update_alarm.call_args_list
|
||||||
self.assertEqual(expected, update_calls)
|
self.assertEqual(expected, update_calls)
|
||||||
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[-1][2],
|
||||||
'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[-1][2],
|
||||||
'Transition to ok due to 6 samples inside'
|
'Transition to ok due to 6 samples inside'
|
||||||
' threshold, most recent: %s' % avgs2.values[-1]]
|
' threshold, most recent: %s' % avgs2[-1][2]]
|
||||||
reason_datas = [self._reason_data('inside', 5, avgs.values[-1]),
|
reason_datas = [self._reason_data('inside', 5, avgs[-1][2]),
|
||||||
self._reason_data('inside', 4, maxs.values[-1]),
|
self._reason_data('inside', 4, maxs[-1][2]),
|
||||||
self._reason_data('inside', 6, avgs2.values[-1])]
|
self._reason_data('inside', 6, avgs2[-1][2])]
|
||||||
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)]
|
||||||
@ -288,8 +267,8 @@ class TestGnocchiThresholdEvaluate(base.TestEvaluatorBase):
|
|||||||
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
|
avgs2 = self._get_stats(50, [self.alarms[2].rule['threshold'] + v
|
||||||
for v in moves.xrange(6)])
|
for v in moves.xrange(6)])
|
||||||
self.requests.post.side_effect = [avgs2]
|
self.client.metric.get_measures.side_effect = [avgs]
|
||||||
self.requests.get.side_effect = [avgs, maxs]
|
self.client.metric.aggregation.side_effect = [maxs, avgs2]
|
||||||
self._evaluate_all_alarms()
|
self._evaluate_all_alarms()
|
||||||
self._assert_all_alarms('ok')
|
self._assert_all_alarms('ok')
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
@ -304,7 +283,7 @@ class TestGnocchiThresholdEvaluate(base.TestEvaluatorBase):
|
|||||||
# the test if the evaluator doesn't remove it.
|
# the test if the evaluator doesn't remove it.
|
||||||
maxs = self._get_stats(300, [self.alarms[0].rule['threshold'] - v
|
maxs = self._get_stats(300, [self.alarms[0].rule['threshold'] - v
|
||||||
for v in moves.xrange(-1, 5)])
|
for v in moves.xrange(-1, 5)])
|
||||||
self.requests.get.side_effect = [maxs]
|
self.client.metric.aggregation.side_effect = [maxs]
|
||||||
self._evaluate_all_alarms()
|
self._evaluate_all_alarms()
|
||||||
self._assert_all_alarms('alarm')
|
self._assert_all_alarms('alarm')
|
||||||
|
|
||||||
@ -317,8 +296,8 @@ class TestGnocchiThresholdEvaluate(base.TestEvaluatorBase):
|
|||||||
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
|
avgs2 = self._get_stats(50, [self.alarms[2].rule['threshold'] + v
|
||||||
for v in moves.xrange(6)])
|
for v in moves.xrange(6)])
|
||||||
self.requests.post.side_effect = [avgs2]
|
self.client.metric.get_measures.side_effect = [avgs]
|
||||||
self.requests.get.side_effect = [avgs, maxs]
|
self.client.metric.aggregation.side_effect = [maxs, avgs2]
|
||||||
self._evaluate_all_alarms()
|
self._evaluate_all_alarms()
|
||||||
self._assert_all_alarms('ok')
|
self._assert_all_alarms('ok')
|
||||||
self.assertEqual([], self.storage_conn.update_alarm.call_args_list)
|
self.assertEqual([], self.storage_conn.update_alarm.call_args_list)
|
||||||
@ -337,8 +316,8 @@ class TestGnocchiThresholdEvaluate(base.TestEvaluatorBase):
|
|||||||
for v in moves.xrange(4)])
|
for v in moves.xrange(4)])
|
||||||
avgs2 = self._get_stats(50, [self.alarms[2].rule['threshold'] + v
|
avgs2 = self._get_stats(50, [self.alarms[2].rule['threshold'] + v
|
||||||
for v in moves.xrange(6)])
|
for v in moves.xrange(6)])
|
||||||
self.requests.post.side_effect = [avgs2]
|
self.client.metric.get_measures.side_effect = [avgs]
|
||||||
self.requests.get.side_effect = [avgs, maxs]
|
self.client.metric.aggregation.side_effect = [maxs, avgs2]
|
||||||
self._evaluate_all_alarms()
|
self._evaluate_all_alarms()
|
||||||
self._assert_all_alarms('alarm')
|
self._assert_all_alarms('alarm')
|
||||||
self.assertEqual([], self.storage_conn.update_alarm.call_args_list)
|
self.assertEqual([], self.storage_conn.update_alarm.call_args_list)
|
||||||
@ -359,22 +338,22 @@ class TestGnocchiThresholdEvaluate(base.TestEvaluatorBase):
|
|||||||
for v in moves.xrange(4)])
|
for v in moves.xrange(4)])
|
||||||
avgs2 = self._get_stats(50, [self.alarms[2].rule['threshold'] + v
|
avgs2 = self._get_stats(50, [self.alarms[2].rule['threshold'] + v
|
||||||
for v in moves.xrange(1, 7)])
|
for v in moves.xrange(1, 7)])
|
||||||
self.requests.post.side_effect = [avgs2]
|
self.client.metric.get_measures.side_effect = [avgs]
|
||||||
self.requests.get.side_effect = [avgs, maxs]
|
self.client.metric.aggregation.side_effect = [maxs, avgs2]
|
||||||
self._evaluate_all_alarms()
|
self._evaluate_all_alarms()
|
||||||
self._assert_all_alarms('alarm')
|
self._assert_all_alarms('alarm')
|
||||||
expected = [mock.call(alarm) for alarm in self.alarms]
|
expected = [mock.call(alarm) for alarm in self.alarms]
|
||||||
update_calls = self.storage_conn.update_alarm.call_args_list
|
update_calls = self.storage_conn.update_alarm.call_args_list
|
||||||
self.assertEqual(expected, update_calls)
|
self.assertEqual(expected, update_calls)
|
||||||
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[-1][2],
|
||||||
'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[-1][2],
|
||||||
'Transition to alarm due to 6 samples outside'
|
'Transition to alarm due to 6 samples outside'
|
||||||
' threshold, most recent: %s' % avgs2.values[-1]]
|
' threshold, most recent: %s' % avgs2[-1][2]]
|
||||||
reason_datas = [self._reason_data('outside', 5, avgs.values[-1]),
|
reason_datas = [self._reason_data('outside', 5, avgs[-1][2]),
|
||||||
self._reason_data('outside', 4, maxs.values[-1]),
|
self._reason_data('outside', 4, maxs[-1][2]),
|
||||||
self._reason_data('outside', 6, avgs2.values[-1])]
|
self._reason_data('outside', 6, avgs2[-1][2])]
|
||||||
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)]
|
||||||
@ -388,22 +367,22 @@ class TestGnocchiThresholdEvaluate(base.TestEvaluatorBase):
|
|||||||
for v in moves.xrange(4)])
|
for v in moves.xrange(4)])
|
||||||
avgs2 = self._get_stats(50, [self.alarms[2].rule['threshold'] + v
|
avgs2 = self._get_stats(50, [self.alarms[2].rule['threshold'] + v
|
||||||
for v in moves.xrange(1, 7)])
|
for v in moves.xrange(1, 7)])
|
||||||
self.requests.post.side_effect = [avgs2]
|
self.client.metric.get_measures.side_effect = [avgs]
|
||||||
self.requests.get.side_effect = [avgs, maxs]
|
self.client.metric.aggregation.side_effect = [maxs, avgs2]
|
||||||
self._evaluate_all_alarms()
|
self._evaluate_all_alarms()
|
||||||
self._assert_all_alarms('alarm')
|
self._assert_all_alarms('alarm')
|
||||||
expected = [mock.call(alarm) for alarm in self.alarms]
|
expected = [mock.call(alarm) for alarm in self.alarms]
|
||||||
update_calls = self.storage_conn.update_alarm.call_args_list
|
update_calls = self.storage_conn.update_alarm.call_args_list
|
||||||
self.assertEqual(expected, update_calls)
|
self.assertEqual(expected, update_calls)
|
||||||
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[-1][2],
|
||||||
'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[-1][2],
|
||||||
'Transition to alarm due to 6 samples outside'
|
'Transition to alarm due to 6 samples outside'
|
||||||
' threshold, most recent: %s' % avgs2.values[-1]]
|
' threshold, most recent: %s' % avgs2[-1][2]]
|
||||||
reason_datas = [self._reason_data('outside', 5, avgs.values[-1]),
|
reason_datas = [self._reason_data('outside', 5, avgs[-1][2]),
|
||||||
self._reason_data('outside', 4, maxs.values[-1]),
|
self._reason_data('outside', 4, maxs[-1][2]),
|
||||||
self._reason_data('outside', 6, avgs2.values[-1])]
|
self._reason_data('outside', 6, avgs2[-1][2])]
|
||||||
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
|
||||||
@ -427,7 +406,8 @@ class TestGnocchiThresholdEvaluate(base.TestEvaluatorBase):
|
|||||||
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)
|
||||||
self.requests.get.return_value = []
|
self.client.metric.get_measures.return_value = []
|
||||||
|
self.client.metric.aggregation.return_value = []
|
||||||
self._evaluate_all_alarms()
|
self._evaluate_all_alarms()
|
||||||
self._assert_all_alarms('ok')
|
self._assert_all_alarms('ok')
|
||||||
update_calls = self.storage_conn.update_alarm.call_args_list
|
update_calls = self.storage_conn.update_alarm.call_args_list
|
||||||
|
@ -6,6 +6,7 @@ retrying!=1.3.0,>=1.2.3 # Apache-2.0
|
|||||||
croniter>=0.3.4 # MIT License
|
croniter>=0.3.4 # MIT License
|
||||||
jsonschema!=2.5.0,<3.0.0,>=2.0.0
|
jsonschema!=2.5.0,<3.0.0,>=2.0.0
|
||||||
keystonemiddleware>=2.2.0
|
keystonemiddleware>=2.2.0
|
||||||
|
gnocchiclient>=2.1.0 # Apache-2.0
|
||||||
lxml>=2.3
|
lxml>=2.3
|
||||||
oslo.context>=0.2.0 # Apache-2.0
|
oslo.context>=0.2.0 # Apache-2.0
|
||||||
oslo.db>=1.12.0 # Apache-2.0
|
oslo.db>=1.12.0 # Apache-2.0
|
||||||
|
Loading…
Reference in New Issue
Block a user