Merge "Add validate alarm_actions schema in alarm API"

This commit is contained in:
Jenkins 2014-09-15 14:33:15 +00:00 committed by Gerrit Code Review
commit 500388c3fe
3 changed files with 122 additions and 2 deletions

View File

@ -232,13 +232,15 @@ class AlarmNotifierService(os_service.Service):
EXTENSIONS_NAMESPACE = "ceilometer.alarm.notifier" EXTENSIONS_NAMESPACE = "ceilometer.alarm.notifier"
notifiers = extension.ExtensionManager(EXTENSIONS_NAMESPACE,
invoke_on_load=True)
notifiers_schemas = notifiers.map(lambda x: x.name)
def __init__(self): def __init__(self):
super(AlarmNotifierService, self).__init__() super(AlarmNotifierService, self).__init__()
transport = messaging.get_transport() transport = messaging.get_transport()
self.rpc_server = messaging.get_rpc_server( self.rpc_server = messaging.get_rpc_server(
transport, cfg.CONF.alarm.notifier_rpc_topic, self) transport, cfg.CONF.alarm.notifier_rpc_topic, self)
self.notifiers = extension.ExtensionManager(self.EXTENSIONS_NAMESPACE,
invoke_on_load=True)
def start(self): def start(self):
super(AlarmNotifierService, self).start() super(AlarmNotifierService, self).start()

View File

@ -37,6 +37,7 @@ import pytz
import uuid import uuid
from oslo.config import cfg from oslo.config import cfg
from oslo.utils import netutils
from oslo.utils import strutils from oslo.utils import strutils
from oslo.utils import timeutils from oslo.utils import timeutils
import pecan import pecan
@ -46,6 +47,7 @@ import wsme
from wsme import types as wtypes from wsme import types as wtypes
import wsmeext.pecan as wsme_pecan import wsmeext.pecan as wsme_pecan
from ceilometer.alarm import service as alarm_service
from ceilometer.alarm.storage import models as alarm_models from ceilometer.alarm.storage import models as alarm_models
from ceilometer.api import acl from ceilometer.api import acl
from ceilometer import messaging from ceilometer import messaging
@ -1815,6 +1817,7 @@ class Alarm(_Base):
def validate(alarm): def validate(alarm):
Alarm.check_rule(alarm) Alarm.check_rule(alarm)
Alarm.check_alarm_actions(alarm)
if alarm.threshold_rule: if alarm.threshold_rule:
# ensure an implicit constraint on project_id is added to # ensure an implicit constraint on project_id is added to
# the query if not already present # the query if not already present
@ -1853,6 +1856,25 @@ class Alarm(_Base):
"cannot be set at the same time") "cannot be set at the same time")
raise ClientSideError(error) raise ClientSideError(error)
@staticmethod
def check_alarm_actions(alarm):
actions_schema = alarm_service.AlarmNotifierService.notifiers_schemas
for state in state_kind:
actions_name = state.replace(" ", "_") + '_actions'
actions = getattr(alarm, actions_name)
if not actions:
continue
for action in actions:
try:
url = netutils.urlsplit(action)
except Exception:
error = _("Unable to parse action %s") % action
raise ClientSideError(error)
if url.scheme not in actions_schema:
error = _("Unsupported action %s") % action
raise ClientSideError(error)
@classmethod @classmethod
def sample(cls): def sample(cls):
return cls(alarm_id=None, return cls(alarm_id=None,

View File

@ -628,6 +628,64 @@ class TestAlarms(v2.FunctionalTest,
'not valid for this resource', 'not valid for this resource',
resp.json['error_message']['faultstring']) resp.json['error_message']['faultstring'])
def _do_post_alarm_invalid_action(self, ok_actions=[], alarm_actions=[],
insufficient_data_actions=[],
error_message=None):
json = {
'enabled': False,
'name': 'added_alarm',
'state': 'ok',
'type': 'threshold',
'ok_actions': ok_actions,
'alarm_actions': alarm_actions,
'insufficient_data_actions': insufficient_data_actions,
'repeat_actions': True,
'threshold_rule': {
'meter_name': 'ameter',
'query': [{'field': 'metadata.field',
'op': 'eq',
'value': '5',
'type': 'string'}],
'comparison_operator': 'le',
'statistic': 'count',
'threshold': 50,
'evaluation_periods': '3',
'period': '180',
}
}
resp = self.post_json('/alarms', params=json, status=400,
headers=self.auth_headers)
alarms = list(self.alarm_conn.get_alarms())
self.assertEqual(4, len(alarms))
self.assertEqual(error_message,
resp.json['error_message']['faultstring'])
def test_post_invalid_alarm_ok_actions(self):
self._do_post_alarm_invalid_action(
ok_actions=['spam://something/ok'],
error_message='Unsupported action spam://something/ok')
def test_post_invalid_alarm_alarm_actions(self):
self._do_post_alarm_invalid_action(
alarm_actions=['spam://something/alarm'],
error_message='Unsupported action spam://something/alarm')
def test_post_invalid_alarm_insufficient_data_actions(self):
self._do_post_alarm_invalid_action(
insufficient_data_actions=['spam://something/insufficient'],
error_message='Unsupported action spam://something/insufficient')
@staticmethod
def _fake_urlsplit(*args, **kwargs):
raise Exception("Evil urlsplit!")
def test_post_invalid_alarm_actions_format(self):
with mock.patch('oslo.utils.netutils.urlsplit',
self._fake_urlsplit):
self._do_post_alarm_invalid_action(
alarm_actions=['http://[::1'],
error_message='Unable to parse action http://[::1')
def test_post_alarm_defaults(self): def test_post_alarm_defaults(self):
to_check = { to_check = {
'enabled': True, 'enabled': True,
@ -1489,6 +1547,44 @@ class TestAlarms(v2.FunctionalTest,
'Alarm with name=name1 exists', 'Alarm with name=name1 exists',
resp.json['error_message']['faultstring']) resp.json['error_message']['faultstring'])
def test_put_invalid_alarm_actions(self):
json = {
'enabled': False,
'name': 'name1',
'state': 'ok',
'type': 'threshold',
'ok_actions': ['spam://something/ok'],
'alarm_actions': ['http://something/alarm'],
'insufficient_data_actions': ['http://something/no'],
'repeat_actions': True,
'threshold_rule': {
'meter_name': 'ameter',
'query': [{'field': 'metadata.field',
'op': 'eq',
'value': '5',
'type': 'string'}],
'comparison_operator': 'le',
'statistic': 'count',
'threshold': 50,
'evaluation_periods': 3,
'period': 180,
}
}
data = self.get_json('/alarms',
q=[{'field': 'name',
'value': 'name2',
}])
self.assertEqual(1, len(data))
alarm_id = data[0]['alarm_id']
resp = self.put_json('/alarms/%s' % alarm_id,
expect_errors=True, status=400,
params=json,
headers=self.auth_headers)
self.assertEqual(
'Unsupported action spam://something/ok',
resp.json['error_message']['faultstring'])
def test_put_alarm_combination_cannot_specify_itself(self): def test_put_alarm_combination_cannot_specify_itself(self):
json = { json = {
'name': 'name4', 'name': 'name4',