fc7e781344
Change-Id: I85d5c8d3d9f00f86fd14500be0da1289f5672614
132 lines
4.6 KiB
Python
132 lines
4.6 KiB
Python
#
|
|
# Copyright 2013 eNovance <licensing@enovance.com>
|
|
#
|
|
# Authors: Mehdi Abaakouk <mehdi.abaakouk@enovance.com>
|
|
#
|
|
# 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.
|
|
|
|
|
|
import abc
|
|
import datetime
|
|
|
|
from ceilometerclient import client as ceiloclient
|
|
import croniter
|
|
from oslo_config import cfg
|
|
from oslo_utils import timeutils
|
|
import pytz
|
|
import six
|
|
|
|
from ceilometer.i18n import _
|
|
from ceilometer.openstack.common import log
|
|
|
|
|
|
LOG = log.getLogger(__name__)
|
|
|
|
UNKNOWN = 'insufficient data'
|
|
OK = 'ok'
|
|
ALARM = 'alarm'
|
|
|
|
cfg.CONF.import_opt('http_timeout', 'ceilometer.service')
|
|
cfg.CONF.import_group('service_credentials', 'ceilometer.service')
|
|
|
|
|
|
@six.add_metaclass(abc.ABCMeta)
|
|
class Evaluator(object):
|
|
"""Base class for alarm rule evaluator plugins."""
|
|
|
|
def __init__(self, notifier):
|
|
self.notifier = notifier
|
|
self.api_client = None
|
|
|
|
@property
|
|
def _client(self):
|
|
"""Construct or reuse an authenticated API client."""
|
|
if not self.api_client:
|
|
auth_config = cfg.CONF.service_credentials
|
|
creds = dict(
|
|
os_auth_url=auth_config.os_auth_url,
|
|
os_region_name=auth_config.os_region_name,
|
|
os_tenant_name=auth_config.os_tenant_name,
|
|
os_password=auth_config.os_password,
|
|
os_username=auth_config.os_username,
|
|
os_cacert=auth_config.os_cacert,
|
|
os_endpoint_type=auth_config.os_endpoint_type,
|
|
insecure=auth_config.insecure,
|
|
timeout=cfg.CONF.http_timeout,
|
|
)
|
|
self.api_client = ceiloclient.get_client(2, **creds)
|
|
return self.api_client
|
|
|
|
def _refresh(self, alarm, state, reason, reason_data):
|
|
"""Refresh alarm state."""
|
|
try:
|
|
previous = alarm.state
|
|
if previous != state:
|
|
LOG.info(_('alarm %(id)s transitioning to %(state)s because '
|
|
'%(reason)s') % {'id': alarm.alarm_id,
|
|
'state': state,
|
|
'reason': reason})
|
|
|
|
self._client.alarms.set_state(alarm.alarm_id, state=state)
|
|
alarm.state = state
|
|
if self.notifier:
|
|
self.notifier.notify(alarm, previous, reason, reason_data)
|
|
except Exception:
|
|
# retry will occur naturally on the next evaluation
|
|
# cycle (unless alarm state reverts in the meantime)
|
|
LOG.exception(_('alarm state update failed'))
|
|
|
|
@classmethod
|
|
def within_time_constraint(cls, alarm):
|
|
"""Check whether the alarm is within at least one of its time limits.
|
|
|
|
If there are none, then the answer is yes.
|
|
"""
|
|
if not alarm.time_constraints:
|
|
return True
|
|
|
|
now_utc = timeutils.utcnow().replace(tzinfo=pytz.utc)
|
|
for tc in alarm.time_constraints:
|
|
tz = pytz.timezone(tc['timezone']) if tc['timezone'] else None
|
|
now_tz = now_utc.astimezone(tz) if tz else now_utc
|
|
start_cron = croniter.croniter(tc['start'], now_tz)
|
|
if cls._is_exact_match(start_cron, now_tz):
|
|
return True
|
|
latest_start = start_cron.get_prev(datetime.datetime)
|
|
duration = datetime.timedelta(seconds=tc['duration'])
|
|
if latest_start <= now_tz <= latest_start + duration:
|
|
return True
|
|
return False
|
|
|
|
@staticmethod
|
|
def _is_exact_match(cron, ts):
|
|
"""Handle edge in case when both parameters are equal.
|
|
|
|
Handle edge case where if the timestamp is the same as the
|
|
cron point in time to the minute, croniter returns the previous
|
|
start, not the current. We can check this by first going one
|
|
step back and then one step forward and check if we are
|
|
at the original point in time.
|
|
"""
|
|
cron.get_prev()
|
|
diff = timeutils.total_seconds(ts - cron.get_next(datetime.datetime))
|
|
return abs(diff) < 60 # minute precision
|
|
|
|
@abc.abstractmethod
|
|
def evaluate(self, alarm):
|
|
"""Interface definition.
|
|
|
|
evaluate an alarm
|
|
alarm Alarm: an instance of the Alarm
|
|
"""
|