Implementation of the alarm RPCAlarmNotifier
This change implement the RPCAlarmNotifier to allow the threshold-evaluator to communicate with the notifier. Change-Id: I1dd3b5aabe5221a37903a4e87de64702aec94ad2
This commit is contained in:
parent
23f66f7e0d
commit
88b4cf0812
47
ceilometer/alarm/rpc.py
Normal file
47
ceilometer/alarm/rpc.py
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
# -*- encoding: utf-8 -*-
|
||||||
|
#
|
||||||
|
# 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.
|
||||||
|
|
||||||
|
from oslo.config import cfg
|
||||||
|
|
||||||
|
from ceilometer.openstack.common import context
|
||||||
|
from ceilometer.openstack.common.rpc import proxy as rpc_proxy
|
||||||
|
from ceilometer.storage.models import Alarm
|
||||||
|
|
||||||
|
OPTS = [
|
||||||
|
cfg.StrOpt('notifier_rpc_topic',
|
||||||
|
default='alarm_notifier',
|
||||||
|
help='the topic ceilometer uses for alarm notifier messages'),
|
||||||
|
]
|
||||||
|
|
||||||
|
cfg.CONF.register_opts(OPTS, group='alarm')
|
||||||
|
|
||||||
|
|
||||||
|
class RPCAlarmNotifier(rpc_proxy.RpcProxy):
|
||||||
|
def __init__(self):
|
||||||
|
super(RPCAlarmNotifier, self).__init__(
|
||||||
|
default_version='1.0',
|
||||||
|
topic=cfg.CONF.alarm.notifier_rpc_topic)
|
||||||
|
|
||||||
|
def notify(self, alarm, state, reason):
|
||||||
|
actions = getattr(alarm, Alarm.ALARM_ACTIONS_MAP[alarm.state])
|
||||||
|
msg = self.make_msg('notify_alarm', data={
|
||||||
|
'actions': actions,
|
||||||
|
'alarm': alarm.alarm_id,
|
||||||
|
'state': state,
|
||||||
|
'reason': reason})
|
||||||
|
self.cast(context.get_admin_context(), msg)
|
@ -21,12 +21,14 @@
|
|||||||
from oslo.config import cfg
|
from oslo.config import cfg
|
||||||
from stevedore import extension
|
from stevedore import extension
|
||||||
|
|
||||||
|
from ceilometer.alarm import rpc as rpc_alarm
|
||||||
from ceilometer.service import prepare_service
|
from ceilometer.service import prepare_service
|
||||||
from ceilometer.openstack.common import log
|
from ceilometer.openstack.common import log
|
||||||
from ceilometer.openstack.common import network_utils
|
from ceilometer.openstack.common import network_utils
|
||||||
from ceilometer.openstack.common import service as os_service
|
from ceilometer.openstack.common import service as os_service
|
||||||
from ceilometer.openstack.common.gettextutils import _
|
from ceilometer.openstack.common.gettextutils import _
|
||||||
from ceilometer.openstack.common.rpc import service as rpc_service
|
from ceilometer.openstack.common.rpc import service as rpc_service
|
||||||
|
from ceilometer.openstack.common.rpc import dispatcher as rpc_dispatcher
|
||||||
from ceilometerclient import client as ceiloclient
|
from ceilometerclient import client as ceiloclient
|
||||||
|
|
||||||
|
|
||||||
@ -39,6 +41,8 @@ OPTS = [
|
|||||||
]
|
]
|
||||||
|
|
||||||
cfg.CONF.register_opts(OPTS, group='alarm')
|
cfg.CONF.register_opts(OPTS, group='alarm')
|
||||||
|
cfg.CONF.import_opt('notifier_rpc_topic', 'ceilometer.alarm.rpc',
|
||||||
|
group='alarm')
|
||||||
|
|
||||||
LOG = log.getLogger(__name__)
|
LOG = log.getLogger(__name__)
|
||||||
|
|
||||||
@ -52,6 +56,7 @@ class SingletonAlarmService(os_service.Service):
|
|||||||
self.extension_manager = extension.ExtensionManager(
|
self.extension_manager = extension.ExtensionManager(
|
||||||
namespace=self.ALARM_NAMESPACE,
|
namespace=self.ALARM_NAMESPACE,
|
||||||
invoke_on_load=True,
|
invoke_on_load=True,
|
||||||
|
invoke_args=(rpc_alarm.RPCAlarmNotifier(),)
|
||||||
)
|
)
|
||||||
|
|
||||||
def start(self):
|
def start(self):
|
||||||
@ -114,6 +119,14 @@ class AlarmNotifierService(rpc_service.Service):
|
|||||||
# Add a dummy thread to have wait() working
|
# Add a dummy thread to have wait() working
|
||||||
self.tg.add_timer(604800, lambda: None)
|
self.tg.add_timer(604800, lambda: None)
|
||||||
|
|
||||||
|
def initialize_service_hook(self, service):
|
||||||
|
LOG.debug('initialize_service_hooks')
|
||||||
|
self.conn.create_worker(
|
||||||
|
cfg.CONF.alarm.notifier_rpc_topic,
|
||||||
|
rpc_dispatcher.RpcDispatcher([self]),
|
||||||
|
'ceilometer.alarm.' + cfg.CONF.alarm.notifier_rpc_topic,
|
||||||
|
)
|
||||||
|
|
||||||
def _handle_action(self, action, alarm, state, reason):
|
def _handle_action(self, action, alarm, state, reason):
|
||||||
try:
|
try:
|
||||||
action = network_utils.urlsplit(action)
|
action = network_utils.urlsplit(action)
|
||||||
|
@ -232,6 +232,13 @@ class Alarm(Model):
|
|||||||
ALARM_INSUFFICIENT_DATA = 'insufficient data'
|
ALARM_INSUFFICIENT_DATA = 'insufficient data'
|
||||||
ALARM_OK = 'ok'
|
ALARM_OK = 'ok'
|
||||||
ALARM_ALARM = 'alarm'
|
ALARM_ALARM = 'alarm'
|
||||||
|
|
||||||
|
ALARM_ACTIONS_MAP = {
|
||||||
|
ALARM_INSUFFICIENT_DATA: 'insufficient_data_actions',
|
||||||
|
ALARM_OK: 'ok_actions',
|
||||||
|
ALARM_ALARM: 'alarm_actions',
|
||||||
|
}
|
||||||
|
|
||||||
"""
|
"""
|
||||||
An alarm to monitor.
|
An alarm to monitor.
|
||||||
|
|
||||||
|
@ -573,6 +573,15 @@
|
|||||||
#rest_notifier_certificate_key=
|
#rest_notifier_certificate_key=
|
||||||
|
|
||||||
|
|
||||||
|
#
|
||||||
|
# Options defined in ceilometer.alarm.rpc
|
||||||
|
#
|
||||||
|
|
||||||
|
# the topic ceilometer uses for alarm notifier messages
|
||||||
|
# (string value)
|
||||||
|
#notifier_rpc_topic=alarm_notifier
|
||||||
|
|
||||||
|
|
||||||
#
|
#
|
||||||
# Options defined in ceilometer.alarm.service
|
# Options defined in ceilometer.alarm.service
|
||||||
#
|
#
|
||||||
@ -700,4 +709,4 @@
|
|||||||
#password=<None>
|
#password=<None>
|
||||||
|
|
||||||
|
|
||||||
# Total option count: 132
|
# Total option count: 133
|
||||||
|
@ -32,6 +32,14 @@ class TestAlarmNotifier(base.TestCase):
|
|||||||
super(TestAlarmNotifier, self).setUp()
|
super(TestAlarmNotifier, self).setUp()
|
||||||
self.service = service.AlarmNotifierService('somehost', 'sometopic')
|
self.service = service.AlarmNotifierService('somehost', 'sometopic')
|
||||||
|
|
||||||
|
@mock.patch('ceilometer.pipeline.setup_pipeline', mock.MagicMock())
|
||||||
|
def test_init_host(self):
|
||||||
|
# If we try to create a real RPC connection, init_host() never
|
||||||
|
# returns. Mock it out so we can establish the service
|
||||||
|
# configuration.
|
||||||
|
with mock.patch('ceilometer.openstack.common.rpc.create_connection'):
|
||||||
|
self.service.start()
|
||||||
|
|
||||||
def test_notify_alarm(self):
|
def test_notify_alarm(self):
|
||||||
data = {
|
data = {
|
||||||
'actions': ['test://'],
|
'actions': ['test://'],
|
||||||
|
90
tests/alarm/test_rpc.py
Normal file
90
tests/alarm/test_rpc.py
Normal file
@ -0,0 +1,90 @@
|
|||||||
|
# -*- encoding: utf-8 -*-
|
||||||
|
#
|
||||||
|
# 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 uuid
|
||||||
|
|
||||||
|
from oslo.config import cfg
|
||||||
|
|
||||||
|
from ceilometer.alarm import rpc as rpc_alarm
|
||||||
|
from ceilometer.openstack.common import rpc
|
||||||
|
from ceilometer.storage.models import Alarm as AlarmModel
|
||||||
|
from ceilometer.tests import base
|
||||||
|
from ceilometerclient.v2.alarms import Alarm as AlarmClient
|
||||||
|
|
||||||
|
|
||||||
|
class TestRPCAlarmNotifier(base.TestCase):
|
||||||
|
def faux_cast(self, context, topic, msg):
|
||||||
|
self.notified.append((topic, msg))
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
super(TestRPCAlarmNotifier, self).setUp()
|
||||||
|
self.notified = []
|
||||||
|
self.stubs.Set(rpc, 'cast', self.faux_cast)
|
||||||
|
self.notifier = rpc_alarm.RPCAlarmNotifier()
|
||||||
|
self.alarms = [
|
||||||
|
AlarmClient(None, info={
|
||||||
|
'name': 'instance_running_hot',
|
||||||
|
'counter_name': 'cpu_util',
|
||||||
|
'comparison_operator': 'gt',
|
||||||
|
'threshold': 80.0,
|
||||||
|
'evaluation_periods': 5,
|
||||||
|
'statistic': 'avg',
|
||||||
|
'state': 'ok',
|
||||||
|
'ok_actions': ['http://host:8080/path'],
|
||||||
|
'user_id': 'foobar',
|
||||||
|
'project_id': 'snafu',
|
||||||
|
'period': 60,
|
||||||
|
'alarm_id': str(uuid.uuid4()),
|
||||||
|
'matching_metadata':{'resource_id':
|
||||||
|
'my_instance'}
|
||||||
|
}),
|
||||||
|
AlarmClient(None, info={
|
||||||
|
'name': 'group_running_idle',
|
||||||
|
'counter_name': 'cpu_util',
|
||||||
|
'comparison_operator': 'le',
|
||||||
|
'threshold': 10.0,
|
||||||
|
'statistic': 'max',
|
||||||
|
'evaluation_periods': 4,
|
||||||
|
'state': 'insufficient data',
|
||||||
|
'insufficient_data_actions': ['http://other_host/path'],
|
||||||
|
'user_id': 'foobar',
|
||||||
|
'project_id': 'snafu',
|
||||||
|
'period': 300,
|
||||||
|
'alarm_id': str(uuid.uuid4()),
|
||||||
|
'matching_metadata':{'metadata.user_metadata.AS':
|
||||||
|
'my_group'}
|
||||||
|
}),
|
||||||
|
]
|
||||||
|
|
||||||
|
def test_notify_alarm(self):
|
||||||
|
for i, a in enumerate(self.alarms):
|
||||||
|
self.notifier.notify(a, "ok", "what? %d" % i)
|
||||||
|
self.assertEqual(len(self.notified), 2)
|
||||||
|
for i, a in enumerate(self.alarms):
|
||||||
|
actions = getattr(a, AlarmModel.ALARM_ACTIONS_MAP[a.state])
|
||||||
|
self.assertEqual(self.notified[i][0],
|
||||||
|
cfg.CONF.alarm.notifier_rpc_topic)
|
||||||
|
self.assertEqual(self.notified[i][1]["args"]["data"]["alarm"],
|
||||||
|
self.alarms[i].alarm_id)
|
||||||
|
self.assertEqual(self.notified[i][1]["args"]["data"]["actions"],
|
||||||
|
actions)
|
||||||
|
self.assertEqual(self.notified[i][1]["args"]["data"]["state"],
|
||||||
|
"ok")
|
||||||
|
self.assertEqual(self.notified[i][1]["args"]["data"]["reason"],
|
||||||
|
"what? %d" % i)
|
Loading…
x
Reference in New Issue
Block a user