monasca-api/monasca_api/common/repositories/model/sub_alarm_definition.py
Tomasz Trębski 0cce983d95 (Non)deterministic support for alarms
The deterministic alarm's main trait
is preventing from transition to UNDETERMINTED state.
That allows effectively create alarms for metrics
which behaviour is similar to events (i.e. generated
upon critical situation in the system). By default (non-deterministic)
alarms created for those metrics would stay in UNDETERMINED stay
for most of the time.

Implements: blueprint alarmsonlogs
Change-Id: I506fcd0b9861e902a2bfcfa768d402c568b85cea
2016-06-08 09:24:21 +02:00

151 lines
5.3 KiB
Python

# Copyright 2014 Hewlett-Packard
#
# 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.
class SubAlarmDefinition(object):
"""Holds sub alarm definition
Used for comparing sub alarm definitions for equality.
"""
def __init__(self, row=None, sub_expr=None):
"""Initialize
:param row: Database row
:param sub_expr: Result from expression parser
:return:
"""
super(SubAlarmDefinition, self).__init__()
if row and sub_expr:
raise Exception('Only one of row or sub_expr can be specified, '
'not both')
if row:
# id is not used for compare or hash.
self.id = row['id']
self.alarm_definition_id = row['alarm_definition_id']
self.metric_name = row['metric_name']
self.dimensions_str = row['dimensions']
self.dimensions = self._init_dimensions(row['dimensions'])
self.function = row['function']
self.operator = row['operator']
# Make sure that the following are converted to unicode
self.period = str(row['period']).decode('utf8')
self.periods = str(row['periods']).decode('utf8')
# threshold comes from the DB as a float in 0.0 form.
self.threshold = str(row['threshold']).decode('utf8')
self.deterministic = str(row['is_deterministic']) == '1'
if sub_expr:
# id is not used for compare or hash.
self.id = ''
# Must be injected.
self.alarm_definition_id = ''
self.metric_name = sub_expr.metric_name
self.dimensions_str = sub_expr.dimensions_str
self.dimensions = self._init_dimensions(sub_expr.dimensions_str)
self.function = sub_expr.normalized_func.decode('utf8')
self.operator = sub_expr.normalized_operator
self.period = sub_expr.period
self.periods = sub_expr.periods
self.threshold = sub_expr.threshold
self.deterministic = sub_expr.deterministic
def _init_dimensions(self, dimensions_str):
dimensions = {}
if dimensions_str:
for dimension in dimensions_str.split(','):
name, value = dimension.split('=')
dimensions[name] = value
return dimensions
@property
def expression(self):
"""Build the entire expressions as a string with spaces."""
result = "{}({}".format(self.function.lower().encode('utf8'),
self.metric_name.encode('utf8'))
if self.dimensions_str:
result += "{{{}}}".format(self.dimensions_str.encode('utf8'))
if self.deterministic:
result += ', deterministic'
if self.period:
result += ", {}".format(self.period.encode('utf8'))
result += ")"
result += " {} {}".format(self.operator.encode('utf8'),
self.threshold.encode('utf8'))
if self.periods:
result += " times {}".format(self.periods.encode('utf8'))
return result.decode('utf8')
def __hash__(self):
dimensions_str = "".join(sorted([name + value for name, value in
self.dimensions.iteritems()]))
# don't use id to hash.
return (hash(self.alarm_definition_id) ^
hash(dimensions_str) ^
hash(self.function) ^
hash(self.metric_name) ^
hash(self.operator) ^
hash(self.period) ^
hash(self.periods) ^
hash(self.deterministic) ^
# Convert to float to handle cases like 0.0 == 0
hash(float(self.threshold)))
def __eq__(self, other):
if id(self) == id(other):
return True
if not isinstance(other, SubAlarmDefinition):
return False
# don't use id to compare.
return (self.alarm_definition_id == other.alarm_definition_id and
self.dimensions == other.dimensions and
self.function == other.function and
self.metric_name == other.metric_name and
self.operator == other.operator and
self.period == other.period and
self.periods == other.periods and
self.deterministic == other.deterministic and
# Convert to float to handle cases like 0.0 == 0
float(self.threshold) == float(other.threshold))
def same_key_fields(self, other):
# compare everything but operator, threshold and deterministic
return (self.metric_name == other.metric_name and
self.dimensions == other.dimensions and
self.function == other.function and
self.period == other.period and
self.periods == other.periods)