#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
from ceilometerclient import client as ceiloclient
from ceilometerclient import exc as ceiloexc
import pecan
import wsme
from wsme import types as wtypes
from aodh.api.controllers.v2 import base
from aodh.api.controllers.v2 import utils as v2_utils
from aodh.i18n import _
from aodh import keystone_client
from aodh import storage
[docs]class AlarmThresholdRule(base.AlarmRule):
"""Alarm Threshold Rule
Describe when to trigger the alarm based on computed statistics
"""
meter_name = wsme.wsattr(wtypes.text, mandatory=True)
"The name of the meter"
# FIXME(sileht): default doesn't work
# workaround: default is set in validate method
query = wsme.wsattr([base.Query], default=[])
"""The query to find the data for computing statistics.
Ownership settings are automatically included based on the Alarm owner.
"""
period = wsme.wsattr(wtypes.IntegerType(minimum=1), default=60)
"The time range in seconds over which query"
comparison_operator = base.AdvEnum('comparison_operator', str,
'lt', 'le', 'eq', 'ne', 'ge', 'gt',
default='eq')
"The comparison against the alarm threshold"
threshold = wsme.wsattr(float, mandatory=True)
"The threshold of the alarm"
statistic = base.AdvEnum('statistic', str, 'max', 'min', 'avg', 'sum',
'count', default='avg')
"The statistic to compare to the threshold"
evaluation_periods = wsme.wsattr(wtypes.IntegerType(minimum=1), default=1)
"The number of historical periods to evaluate the threshold"
exclude_outliers = wsme.wsattr(bool, default=False)
"Whether datapoints with anomalously low sample counts are excluded"
ceilometer_sample_api_is_supported = None
def __init__(self, query=None, **kwargs):
query = [base.Query(**q) for q in query] if query else []
super(AlarmThresholdRule, self).__init__(query=query, **kwargs)
@classmethod
def _check_ceilometer_sample_api(cls):
# Check it only once
if cls.ceilometer_sample_api_is_supported is None:
auth_config = pecan.request.cfg.service_credentials
client = ceiloclient.get_client(
version=2,
session=keystone_client.get_session(pecan.request.cfg),
# ceiloclient adapter options
region_name=auth_config.region_name,
interface=auth_config.interface,
)
try:
client.statistics.list(
meter_name="idontthinkthatexistsbutwhatever")
except Exception as e:
if isinstance(e, ceiloexc.HTTPException):
if e.code == 410:
cls.ceilometer_sample_api_is_supported = False
elif e.code < 500:
cls.ceilometer_sample_api_is_supported = True
else:
raise
else:
raise
else:
# I don't think this meter can exist but how known
cls.ceilometer_sample_api_is_supported = True
if cls.ceilometer_sample_api_is_supported is False:
raise base.ClientSideError(
"This telemetry installation is not configured to support"
"alarm of type 'threshold")
@staticmethod
[docs] def validate(threshold_rule):
# note(sileht): wsme default doesn't work in some case
# workaround for https://bugs.launchpad.net/wsme/+bug/1227039
if not threshold_rule.query:
threshold_rule.query = []
# Timestamp is not allowed for AlarmThresholdRule query, as the alarm
# evaluator will construct timestamp bounds for the sequence of
# statistics queries as the sliding evaluation window advances
# over time.
v2_utils.validate_query(threshold_rule.query,
storage.SampleFilter.__init__,
allow_timestamps=False)
return threshold_rule
@classmethod
[docs] def validate_alarm(cls, alarm):
cls._check_ceilometer_sample_api()
# ensure an implicit constraint on project_id is added to
# the query if not already present
alarm.threshold_rule.query = v2_utils.sanitize_query(
alarm.threshold_rule.query,
storage.SampleFilter.__init__,
on_behalf_of=alarm.project_id
)
@property
def default_description(self):
return (_('Alarm when %(meter_name)s is %(comparison_operator)s a '
'%(statistic)s of %(threshold)s over %(period)s seconds') %
dict(comparison_operator=self.comparison_operator,
statistic=self.statistic,
threshold=self.threshold,
meter_name=self.meter_name,
period=self.period))
[docs] def as_dict(self):
rule = self.as_dict_from_keys(['period', 'comparison_operator',
'threshold', 'statistic',
'evaluation_periods', 'meter_name',
'exclude_outliers'])
rule['query'] = [q.as_dict() for q in self.query]
return rule
@classmethod
[docs] def sample(cls):
return cls(meter_name='cpu_util',
period=60,
evaluation_periods=1,
threshold=300.0,
statistic='avg',
comparison_operator='gt',
query=[{'field': 'resource_id',
'value': '2a4d689b-f0b8-49c1-9eef-87cae58d80db',
'op': 'eq',
'type': 'string'}])