Merge "Expose alarm severity in Alarm Model"

This commit is contained in:
Jenkins 2015-01-29 21:48:46 +00:00 committed by Gerrit Code Review
commit 2b55867308
25 changed files with 193 additions and 65 deletions

View File

@ -23,13 +23,14 @@ class AlarmNotifier(object):
"""Base class for alarm notifier plugins."""
@abc.abstractmethod
def notify(self, action, alarm_id, alarm_name, previous, current,
reason, reason_data):
def notify(self, action, alarm_id, alarm_name, severity, previous,
current, reason, reason_data):
"""Notify that an alarm has been triggered.
:param action: The action that is being attended, as a parsed URL.
:param alarm_id: The triggered alarm.
:param alarm_name: The name of triggered alarm.
:param severity: The level of triggered alarm
:param previous: The previous state of the alarm.
:param current: The current state of the alarm.
:param reason: The reason the alarm changed its state.

View File

@ -25,11 +25,15 @@ class LogAlarmNotifier(notifier.AlarmNotifier):
"Log alarm notifier."""
@staticmethod
def notify(action, alarm_id, alarm_name, previous, current, reason,
reason_data):
def notify(action, alarm_id, alarm_name, severity, previous, current,
reason, reason_data):
LOG.info(_(
"Notifying alarm %(alarm_name)s %(alarm_id)s from %(previous)s "
"to %(current)s with action %(action)s because "
"%(reason)s.") % ({'alarm_name': alarm_name, 'alarm_id': alarm_id,
'previous': previous, 'current': current,
'action': action, 'reason': reason}))
"Notifying alarm %(alarm_name)s %(alarm_id)s of %(severity)s "
"priority from %(previous)s to %(current)s with action %(action)s"
" because %(reason)s.") % ({'alarm_name': alarm_name,
'alarm_id': alarm_id,
'severity': severity,
'previous': previous,
'current': current,
'action': action,
'reason': reason}))

View File

@ -55,8 +55,8 @@ class RestAlarmNotifier(notifier.AlarmNotifier):
"""Rest alarm notifier."""
@staticmethod
def notify(action, alarm_id, alarm_name, previous, current, reason,
reason_data, headers=None):
def notify(action, alarm_id, alarm_name, severity, previous,
current, reason, reason_data, headers=None):
headers = headers or {}
if not headers.get('x-openstack-request-id'):
headers['x-openstack-request-id'] = context.generate_request_id()
@ -66,12 +66,13 @@ class RestAlarmNotifier(notifier.AlarmNotifier):
"%(previous)s to %(current)s with action %(action)s because "
"%(reason)s. request-id: %(request_id)s ") %
({'alarm_name': alarm_name, 'alarm_id': alarm_id,
'previous': previous, 'current': current,
'action': action, 'reason': reason,
'severity': severity, 'previous': previous,
'current': current, 'action': action, 'reason': reason,
'request_id': headers['x-openstack-request-id']}))
body = {'alarm_name': alarm_name, 'alarm_id': alarm_id,
'previous': previous, 'current': current,
'reason': reason, 'reason_data': reason_data}
'severity': severity, 'previous': previous,
'current': current, 'reason': reason,
'reason_data': reason_data}
headers['content-type'] = 'application/json'
kwargs = {'data': jsonutils.dumps(body),
'headers': headers}

View File

@ -23,11 +23,12 @@ class TestAlarmNotifier(notifier.AlarmNotifier):
def __init__(self):
self.notifications = []
def notify(self, action, alarm_id, alarm_name, previous, current,
reason, reason_data):
def notify(self, action, alarm_id, alarm_name, severity,
previous, current, reason, reason_data):
self.notifications.append((action,
alarm_id,
alarm_name,
severity,
previous,
current,
reason,

View File

@ -36,7 +36,7 @@ class TrustRestAlarmNotifier(rest.RestAlarmNotifier):
"""
@staticmethod
def notify(action, alarm_id, alarm_name, previous, current,
def notify(action, alarm_id, alarm_name, severity, previous, current,
reason, reason_data):
trust_id = action.username
@ -62,5 +62,5 @@ class TrustRestAlarmNotifier(rest.RestAlarmNotifier):
headers = {'X-Auth-Token': client.auth_token}
rest.RestAlarmNotifier.notify(
action, alarm_id, alarm_name, previous, current, reason,
action, alarm_id, alarm_name, severity, previous, current, reason,
reason_data, headers)

View File

@ -65,6 +65,7 @@ class RPCAlarmNotifier(object):
'actions': actions,
'alarm_id': alarm.alarm_id,
'alarm_name': alarm.name,
'severity': alarm.severity,
'previous': previous,
'current': alarm.state,
'reason': six.text_type(reason),

View File

@ -243,8 +243,8 @@ class AlarmNotifierService(os_service.Service):
self.rpc_server.stop()
super(AlarmNotifierService, self).stop()
def _handle_action(self, action, alarm_id, alarm_name, previous,
current, reason, reason_data):
def _handle_action(self, action, alarm_id, alarm_name, severity,
previous, current, reason, reason_data):
try:
action = netutils.urlsplit(action)
except Exception:
@ -266,8 +266,8 @@ class AlarmNotifierService(os_service.Service):
try:
LOG.debug(_("Notifying alarm %(id)s with action %(act)s") % (
{'id': alarm_id, 'act': action}))
notifier.notify(action, alarm_id, alarm_name, previous,
current, reason, reason_data)
notifier.notify(action, alarm_id, alarm_name, severity,
previous, current, reason, reason_data)
except Exception:
LOG.exception(_("Unable to notify alarm %s"), alarm_id)
return
@ -282,6 +282,7 @@ class AlarmNotifierService(os_service.Service):
extensions automatically
- alarm_id, the ID of the alarm that has been triggered
- alarm_name, the name of the alarm that has been triggered
- severity, the level of the alarm that has been triggered
- previous, the previous state of the alarm
- current, the new state the alarm has transitioned to
- reason, the reason the alarm changed its state
@ -296,6 +297,7 @@ class AlarmNotifierService(os_service.Service):
self._handle_action(action,
data.get('alarm_id'),
data.get('alarm_name'),
data.get('severity'),
data.get('previous'),
data.get('current'),
data.get('reason'),

View File

@ -42,7 +42,7 @@ class Connection(object):
@staticmethod
def get_alarms(name=None, user=None, state=None, meter=None,
project=None, enabled=None, alarm_id=None, pagination=None,
alarm_type=None):
alarm_type=None, severity=None):
"""Yields a lists of alarms that match filters.
:param name: Optional name for alarm.
@ -54,6 +54,7 @@ class Connection(object):
:param alarm_id: Optional alarm_id to return one alarm.
:param pagination: Optional pagination query.
:param alarm_type: Optional alarm type.
:parmr severity: Optional alarm severity
"""
raise ceilometer.NotImplementedError('Alarms not implemented')
@ -78,8 +79,9 @@ class Connection(object):
@staticmethod
def get_alarm_changes(alarm_id, on_behalf_of,
user=None, project=None, alarm_type=None,
start_timestamp=None, start_timestamp_op=None,
end_timestamp=None, end_timestamp_op=None):
severity=None, start_timestamp=None,
start_timestamp_op=None, end_timestamp=None,
end_timestamp_op=None):
"""Yields list of AlarmChanges describing alarm history
Changes are always sorted in reverse order of occurrence, given
@ -98,6 +100,7 @@ class Connection(object):
:param user: Optional ID of user to return changes for
:param project: Optional ID of project to return changes for
:param alarm_type: Optional change type
:param severity: Optional change severity
:param start_timestamp: Optional modified timestamp start range
:param start_timestamp_op: Optional timestamp start range operation
:param end_timestamp: Optional modified timestamp end range

View File

@ -120,7 +120,7 @@ class Connection(hbase_base.Connection, base.Connection):
def get_alarms(self, name=None, user=None, state=None, meter=None,
project=None, enabled=None, alarm_id=None, pagination=None,
alarm_type=None):
alarm_type=None, severity=None):
if pagination:
raise ceilometer.NotImplementedError('Pagination not implemented')
@ -131,7 +131,7 @@ class Connection(hbase_base.Connection, base.Connection):
q = hbase_utils.make_query(alarm_id=alarm_id, name=name,
enabled=enabled, user_id=user,
project_id=project, state=state,
type=alarm_type)
type=alarm_type, severity=severity)
with self.conn_pool.connection() as conn:
alarm_table = conn.table(self.ALARM_TABLE)
@ -146,11 +146,13 @@ class Connection(hbase_base.Connection, base.Connection):
def get_alarm_changes(self, alarm_id, on_behalf_of,
user=None, project=None, alarm_type=None,
start_timestamp=None, start_timestamp_op=None,
end_timestamp=None, end_timestamp_op=None):
severity=None, start_timestamp=None,
start_timestamp_op=None, end_timestamp=None,
end_timestamp_op=None):
q = hbase_utils.make_query(alarm_id=alarm_id,
on_behalf_of=on_behalf_of, type=alarm_type,
user_id=user, project_id=project)
user_id=user, project_id=project,
severity=severity)
start_row, end_row = hbase_utils.make_timestamp_query(
hbase_utils.make_general_rowkey_scan,
start=start_timestamp, start_op=start_timestamp_op,

View File

@ -32,7 +32,7 @@ class Connection(base.Connection):
def get_alarms(self, name=None, user=None, state=None, meter=None,
project=None, enabled=None, alarm_id=None, pagination=None,
alarm_type=None):
alarm_type=None, severity=None):
"""Yields a lists of alarms that match filters."""
return []

View File

@ -132,14 +132,15 @@ class Connection(base.Connection):
row.insufficient_data_actions),
rule=row.rule,
time_constraints=row.time_constraints,
repeat_actions=row.repeat_actions)
repeat_actions=row.repeat_actions,
severity=row.severity)
def _retrieve_alarms(self, query):
return (self._row_to_alarm_model(x) for x in query.all())
def get_alarms(self, name=None, user=None, state=None, meter=None,
project=None, enabled=None, alarm_id=None, pagination=None,
alarm_type=None):
alarm_type=None, severity=None):
"""Yields a lists of alarms that match filters.
:param name: Optional name for alarm.
@ -151,6 +152,7 @@ class Connection(base.Connection):
:param alarm_id: Optional alarm_id to return one alarm.
:param pagination: Optional pagination query.
:param alarm_type: Optional alarm type.
:param severity: Optional alarm severity
"""
if pagination:
@ -172,6 +174,8 @@ class Connection(base.Connection):
query = query.filter(models.Alarm.state == state)
if alarm_type is not None:
query = query.filter(models.Alarm.type == alarm_type)
if severity is not None:
query = query.filter(models.Alarm.severity == severity)
query = query.order_by(desc(models.Alarm.timestamp))
alarms = self._retrieve_alarms(query)
@ -246,8 +250,9 @@ class Connection(base.Connection):
def get_alarm_changes(self, alarm_id, on_behalf_of,
user=None, project=None, alarm_type=None,
start_timestamp=None, start_timestamp_op=None,
end_timestamp=None, end_timestamp_op=None):
severity=None, start_timestamp=None,
start_timestamp_op=None, end_timestamp=None,
end_timestamp_op=None):
"""Yields list of AlarmChanges describing alarm history
Changes are always sorted in reverse order of occurrence, given
@ -266,6 +271,7 @@ class Connection(base.Connection):
:param user: Optional ID of user to return changes for
:param project: Optional ID of project to return changes for
:param alarm_type: Optional change type
:param severity: Optional alarm severity
:param start_timestamp: Optional modified timestamp start range
:param start_timestamp_op: Optional timestamp start range operation
:param end_timestamp: Optional modified timestamp end range
@ -284,6 +290,8 @@ class Connection(base.Connection):
query = query.filter(models.AlarmChange.project_id == project)
if alarm_type is not None:
query = query.filter(models.AlarmChange.type == alarm_type)
if severity is not None:
query = query.filter(models.AlarmChange.severity == severity)
if start_timestamp:
if start_timestamp_op == 'gt':
query = query.filter(

View File

@ -32,6 +32,10 @@ class Alarm(base.Model):
ALARM_ALARM: 'alarm_actions',
}
ALARM_LEVEL_LOW = 'low'
ALARM_LEVEL_MODERATE = 'moderate'
ALARM_LEVEL_CRITICAL = 'critical'
"""
An alarm to monitor.
@ -56,16 +60,16 @@ class Alarm(base.Model):
entering the insufficient data state
:param repeat_actions: Is the actions should be triggered on each
alarm evaluation.
:param severity: Alarm level (low/moderate/critical)
"""
def __init__(self, alarm_id, type, enabled, name, description,
timestamp, user_id, project_id, state, state_timestamp,
ok_actions, alarm_actions, insufficient_data_actions,
repeat_actions, rule, time_constraints):
repeat_actions, rule, time_constraints, severity=None):
if not isinstance(timestamp, datetime.datetime):
raise TypeError(_("timestamp should be datetime object"))
if not isinstance(state_timestamp, datetime.datetime):
raise TypeError(_("state_timestamp should be datetime object"))
base.Model.__init__(
self,
alarm_id=alarm_id,
@ -83,7 +87,8 @@ class Alarm(base.Model):
insufficient_data_actions=insufficient_data_actions,
repeat_actions=repeat_actions,
rule=rule,
time_constraints=time_constraints)
time_constraints=time_constraints,
severity=severity)
class AlarmChange(base.Model):
@ -92,6 +97,7 @@ class AlarmChange(base.Model):
:param event_id: UUID of the change event
:param alarm_id: UUID of the alarm
:param type: The type of change
:param severity: The severity of alarm
:param detail: JSON fragment describing change
:param user_id: the user ID of the initiating identity
:param project_id: the project ID of the initiating identity
@ -113,6 +119,7 @@ class AlarmChange(base.Model):
user_id,
project_id,
on_behalf_of,
severity=None,
timestamp=None
):
base.Model.__init__(
@ -120,6 +127,7 @@ class AlarmChange(base.Model):
event_id=event_id,
alarm_id=alarm_id,
type=type,
severity=severity,
detail=detail,
user_id=user_id,
project_id=project_id,

View File

@ -81,7 +81,7 @@ class Connection(base.Connection):
def get_alarms(self, name=None, user=None, state=None, meter=None,
project=None, enabled=None, alarm_id=None, pagination=None,
alarm_type=None):
alarm_type=None, severity=None):
"""Yields a lists of alarms that match filters.
:param name: Optional name for alarm.
@ -93,6 +93,7 @@ class Connection(base.Connection):
:param alarm_id: Optional alarm_id to return one alarm.
:param pagination: Optional pagination query.
:param alarm_type: Optional alarm type.
:param severity: Optional alarm severity.
"""
if pagination:
raise ceilometer.NotImplementedError('Pagination not implemented')
@ -114,6 +115,8 @@ class Connection(base.Connection):
q['rule.meter_name'] = meter
if alarm_type is not None:
q['type'] = alarm_type
if severity is not None:
q['severity'] = severity
return self._retrieve_alarms(q,
[("timestamp",
@ -122,8 +125,9 @@ class Connection(base.Connection):
def get_alarm_changes(self, alarm_id, on_behalf_of,
user=None, project=None, alarm_type=None,
start_timestamp=None, start_timestamp_op=None,
end_timestamp=None, end_timestamp_op=None):
severity=None, start_timestamp=None,
start_timestamp_op=None, end_timestamp=None,
end_timestamp_op=None):
"""Yields list of AlarmChanges describing alarm history
Changes are always sorted in reverse order of occurrence, given
@ -142,6 +146,7 @@ class Connection(base.Connection):
:param user: Optional ID of user to return changes for
:param project: Optional ID of project to return changes for
:param alarm_type: Optional change type
:param severity: Optional change severity
:param start_timestamp: Optional modified timestamp start range
:param start_timestamp_op: Optional timestamp start range operation
:param end_timestamp: Optional modified timestamp end range
@ -156,6 +161,8 @@ class Connection(base.Connection):
q['project_id'] = project
if alarm_type is not None:
q['type'] = alarm_type
if severity is not None:
q['severity'] = severity
if start_timestamp or end_timestamp:
ts_range = pymongo_utils.make_timestamp_range(start_timestamp,
end_timestamp,

View File

@ -86,6 +86,8 @@ state_kind = ["ok", "alarm", "insufficient data"]
state_kind_enum = wtypes.Enum(str, *state_kind)
operation_kind = ('lt', 'le', 'eq', 'ne', 'ge', 'gt')
operation_kind_enum = wtypes.Enum(str, *operation_kind)
severity_kind = ["low", "moderate", "critical"]
severity_kind_enum = wtypes.Enum(str, *severity_kind)
class ClientSideError(wsme.exc.ClientSideError):
@ -1832,6 +1834,10 @@ class Alarm(_Base):
state_timestamp = datetime.datetime
"The date of the last alarm state changed"
severity = AdvEnum('severity', str, *severity_kind,
default='low')
"The severity of the alarm"
def __init__(self, rule=None, time_constraints=None, **kwargs):
super(Alarm, self).__init__(**kwargs)
@ -1920,6 +1926,7 @@ class Alarm(_Base):
enabled=True,
timestamp=datetime.datetime.utcnow(),
state="ok",
severity="moderate",
state_timestamp=datetime.datetime.utcnow(),
ok_actions=["http://site:8000/ok"],
alarm_actions=["http://site:8000/alarm"],
@ -2051,6 +2058,7 @@ class AlarmController(rest.RestController):
now = timeutils.utcnow()
data.alarm_id = self._id
user, project = rbac.get_limited_to(pecan.request.headers)
if user:
data.user_id = user
@ -2065,7 +2073,7 @@ class AlarmController(rest.RestController):
data.state_timestamp = now
else:
data.state_timestamp = alarm_in.state_timestamp
alarm_in.severity = data.severity
# make sure alarms are unique by name per project.
if alarm_in.name != data.name:
alarms = list(self.conn.get_alarms(name=data.name,

View File

@ -0,0 +1,31 @@
#
# 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 sqlalchemy import Column
from sqlalchemy import MetaData
from sqlalchemy import String
from sqlalchemy import Table
def upgrade(migrate_engine):
meta = MetaData(bind=migrate_engine)
alarm = Table('alarm', meta, autoload=True)
severity = Column('severity', String(50))
alarm.create_column(severity)
def downgrade(migrate_engine):
meta = MetaData(bind=migrate_engine)
alarm = Table('alarm', meta, autoload=True)
severity = Column('severity', String(50))
alarm.drop_column(severity)

View File

@ -259,6 +259,7 @@ class Alarm(Base):
enabled = Column(Boolean)
name = Column(Text)
type = Column(String(50))
severity = Column(String(50))
description = Column(Text)
timestamp = Column(PreciseTimestamp, default=lambda: timeutils.utcnow())

View File

@ -58,7 +58,8 @@ class TestEvaluate(base.TestEvaluatorBase):
'9cfc3e51-2ff1-4b1d-ac01-c1bd4c6d0d1e',
'1d441595-d069-4e05-95ab-8693ba6a8302'],
operator='or',
)),
),
severity='critical'),
models.Alarm(name='and-alarm',
description='the and alarm',
type='combination',
@ -79,7 +80,8 @@ class TestEvaluate(base.TestEvaluatorBase):
'b82734f4-9d06-48f3-8a86-fa59a0c99dc8',
'15a700e5-2fe8-4b3d-8c55-9e92831f6a2b'],
operator='and',
))
),
severity='critical')
]
@staticmethod

View File

@ -63,7 +63,8 @@ class TestEvaluate(base.TestEvaluatorBase):
'value': 'cpu_util'},
{'field': 'resource_id',
'op': 'eq',
'value': 'my_instance'}])
'value': 'my_instance'}]),
severity='critical'
),
models.Alarm(name='group_running_idle',
description='group_running_idle',
@ -92,7 +93,8 @@ class TestEvaluate(base.TestEvaluatorBase):
'value': 'cpu_util'},
{'field': 'metadata.user_metadata.AS',
'op': 'eq',
'value': 'my_group'}])
'value': 'my_group'}]),
severity='critical'
),
]

View File

@ -121,6 +121,7 @@ class TestCoordinate(tests_base.BaseTestCase):
alarm_actions=[],
insufficient_data_actions=[],
alarm_id=uuid,
severity='critical',
time_constraints=[],
rule=dict(
statistic='avg',

View File

@ -28,11 +28,12 @@ from ceilometer.tests import base as tests_base
DATA_JSON = jsonutils.loads(
'{"current": "ALARM", "alarm_id": "foobar", "alarm_name": "testalarm",'
' "reason": "what ?", "reason_data": {"test": "test"},'
' "previous": "OK"}'
' "severity": "critical", "reason": "what ?",'
' "reason_data": {"test": "test"}, "previous": "OK"}'
)
NOTIFICATION = dict(alarm_id='foobar',
alarm_name='testalarm',
severity='critical',
condition=dict(threshold=42),
reason='what ?',
reason_data={'test': 'test'},
@ -64,6 +65,7 @@ class TestAlarmNotifier(tests_base.BaseTestCase):
'actions': ['test://'],
'alarm_id': 'foobar',
'alarm_name': 'testalarm',
'severity': 'critical',
'previous': 'OK',
'current': 'ALARM',
'reason': 'Everything is on fire',
@ -75,6 +77,7 @@ class TestAlarmNotifier(tests_base.BaseTestCase):
self.assertEqual((urlparse.urlsplit(data['actions'][0]),
data['alarm_id'],
data['alarm_name'],
data['severity'],
data['previous'],
data['current'],
data['reason'],

View File

@ -67,6 +67,7 @@ class TestRPCAlarmNotifier(tests_base.BaseTestCase):
'project_id': 'snafu',
'period': 60,
'alarm_id': str(uuid.uuid4()),
'severity': 'critical',
'matching_metadata':{'resource_id':
'my_instance'}
}),
@ -83,6 +84,7 @@ class TestRPCAlarmNotifier(tests_base.BaseTestCase):
'project_id': 'snafu',
'period': 300,
'alarm_id': str(uuid.uuid4()),
'severity': 'critical',
'matching_metadata':{'metadata.user_metadata.AS':
'my_group'}
}),
@ -109,6 +111,8 @@ class TestRPCAlarmNotifier(tests_base.BaseTestCase):
self.notifier_server.notified[i]["alarm_id"])
self.assertEqual(self.alarms[i].name,
self.notifier_server.notified[i]["alarm_name"])
self.assertEqual(self.alarms[i].severity,
self.notifier_server.notified[i]["severity"])
self.assertEqual(actions,
self.notifier_server.notified[i]["actions"])
self.assertEqual(previous[i],

View File

@ -52,6 +52,7 @@ class TestAlarms(v2.FunctionalTest,
alarm_id='a',
description='a',
state='insufficient data',
severity='critical',
state_timestamp=constants.MIN_DATETIME,
timestamp=constants.MIN_DATETIME,
ok_actions=[],
@ -72,7 +73,7 @@ class TestAlarms(v2.FunctionalTest,
query=[{'field': 'project_id',
'op': 'eq', 'value':
self.auth_headers['X-Project-Id']}
])
]),
),
models.Alarm(name='name2',
type='threshold',
@ -80,6 +81,7 @@ class TestAlarms(v2.FunctionalTest,
alarm_id='b',
description='b',
state='insufficient data',
severity='critical',
state_timestamp=constants.MIN_DATETIME,
timestamp=constants.MIN_DATETIME,
ok_actions=[],
@ -98,7 +100,7 @@ class TestAlarms(v2.FunctionalTest,
query=[{'field': 'project_id',
'op': 'eq', 'value':
self.auth_headers['X-Project-Id']}
])
]),
),
models.Alarm(name='name3',
type='threshold',
@ -106,6 +108,7 @@ class TestAlarms(v2.FunctionalTest,
alarm_id='c',
description='c',
state='insufficient data',
severity='moderate',
state_timestamp=constants.MIN_DATETIME,
timestamp=constants.MIN_DATETIME,
ok_actions=[],
@ -124,7 +127,7 @@ class TestAlarms(v2.FunctionalTest,
query=[{'field': 'project_id',
'op': 'eq', 'value':
self.auth_headers['X-Project-Id']}
])
]),
),
models.Alarm(name='name4',
type='combination',
@ -132,6 +135,7 @@ class TestAlarms(v2.FunctionalTest,
alarm_id='d',
description='d',
state='insufficient data',
severity='low',
state_timestamp=constants.MIN_DATETIME,
timestamp=constants.MIN_DATETIME,
ok_actions=[],
@ -142,7 +146,7 @@ class TestAlarms(v2.FunctionalTest,
project_id=self.auth_headers['X-Project-Id'],
time_constraints=[],
rule=dict(alarm_ids=['a', 'b'],
operator='or')
operator='or'),
)]:
self.alarm_conn.update_alarm(alarm)
@ -218,7 +222,8 @@ class TestAlarms(v2.FunctionalTest,
user_id=self.auth_headers['X-User-Id'],
project_id=self.auth_headers['X-Project-Id'],
time_constraints=[],
rule=dict(alarm_ids=['a', 'b'], operator='or'))
rule=dict(alarm_ids=['a', 'b'], operator='or'),
severity='critical')
self.alarm_conn.update_alarm(alarm)
resp = self.get_json('/alarms',
q=[{'field': 'state',
@ -277,7 +282,8 @@ class TestAlarms(v2.FunctionalTest,
user_id=self.auth_headers['X-User-Id'],
project_id=self.auth_headers['X-Project-Id'],
time_constraints=[],
rule=dict(alarm_ids=['a', 'b'], operator='or'))
rule=dict(alarm_ids=['a', 'b'], operator='or'),
severity='critical')
self.alarm_conn.update_alarm(alarm)
alarms = self.get_json('/alarms',
@ -566,6 +572,27 @@ class TestAlarms(v2.FunctionalTest,
alarms = list(self.alarm_conn.get_alarms())
self.assertEqual(4, len(alarms))
def test_post_invalid_alarm_input_severity(self):
json = {
'name': 'alarm1',
'state': 'ok',
'severity': 'bad_value',
'type': 'threshold',
'threshold_rule': {
'meter_name': 'ameter',
'comparison_operator': 'gt',
'threshold': 50.0
}
}
resp = self.post_json('/alarms', params=json, expect_errors=True,
status=400, headers=self.auth_headers)
expected_err_msg = ("Invalid input for field/attribute severity."
" Value: 'bad_value'.")
self.assertIn(expected_err_msg,
resp.json['error_message']['faultstring'])
alarms = list(self.alarm_conn.get_alarms())
self.assertEqual(4, len(alarms))
def test_post_invalid_alarm_input_comparison_operator(self):
json = {
'name': 'alarm2',
@ -924,6 +951,7 @@ class TestAlarms(v2.FunctionalTest,
'name': 'added_alarm',
'state': 'ok',
'type': 'threshold',
'severity': 'low',
'ok_actions': ['http://something/ok'],
'alarm_actions': ['http://something/alarm'],
'insufficient_data_actions': ['http://something/no'],
@ -971,6 +999,7 @@ class TestAlarms(v2.FunctionalTest,
'name': 'added_alarm',
'state': 'ok',
'type': 'threshold',
'severity': 'low',
'ok_actions': ['http://something/ok'],
'alarm_actions': ['http://something/alarm'],
'insufficient_data_actions': ['http://something/no'],
@ -1539,6 +1568,7 @@ class TestAlarms(v2.FunctionalTest,
'name': 'name_put',
'state': 'ok',
'type': 'threshold',
'severity': 'critical',
'ok_actions': ['http://something/ok'],
'alarm_actions': ['http://something/alarm'],
'insufficient_data_actions': ['http://something/no'],
@ -1581,6 +1611,7 @@ class TestAlarms(v2.FunctionalTest,
'name': 'name_put',
'state': 'ok',
'type': 'threshold',
'severity': 'critical',
'ok_actions': ['http://something/ok'],
'alarm_actions': ['http://something/alarm'],
'insufficient_data_actions': ['http://something/no'],
@ -1630,6 +1661,7 @@ class TestAlarms(v2.FunctionalTest,
'name': 'name1',
'state': 'ok',
'type': 'threshold',
'severity': 'critical',
'ok_actions': ['http://something/ok'],
'alarm_actions': ['http://something/alarm'],
'insufficient_data_actions': ['http://something/no'],
@ -1666,6 +1698,7 @@ class TestAlarms(v2.FunctionalTest,
'name': 'name1',
'state': 'ok',
'type': 'threshold',
'severity': 'critical',
'ok_actions': ['http://something/ok'],
'alarm_actions': ['http://something/alarm'],
'insufficient_data_actions': ['http://something/no'],
@ -1704,6 +1737,7 @@ class TestAlarms(v2.FunctionalTest,
'name': 'name1',
'state': 'ok',
'type': 'threshold',
'severity': 'critical',
'ok_actions': ['spam://something/ok'],
'alarm_actions': ['http://something/alarm'],
'insufficient_data_actions': ['http://something/no'],
@ -2154,10 +2188,11 @@ class TestAlarms(v2.FunctionalTest,
query = dict(field='alarm_id', op='eq', value='b')
resp = self._get_alarm_history(alarm, query=query,
expect_errors=True, status=400)
self.assertEqual('Unknown argument: "alarm_id": unrecognized'
self.assertEqual(u'Unknown argument: "alarm_id": unrecognized'
" field in query: [<Query u'alarm_id' eq"
" u'b' Unset>], valid keys: ['project', "
"'search_offset', 'timestamp', 'type', 'user']",
"'search_offset', 'severity', 'timestamp',"
" 'type', 'user']",
resp.json['error_message']['faultstring'])
def test_get_alarm_history_constrained_by_not_supported_rule(self):
@ -2165,10 +2200,11 @@ class TestAlarms(v2.FunctionalTest,
query = dict(field='abcd', op='eq', value='abcd')
resp = self._get_alarm_history(alarm, query=query,
expect_errors=True, status=400)
self.assertEqual('Unknown argument: "abcd": unrecognized'
self.assertEqual(u'Unknown argument: "abcd": unrecognized'
" field in query: [<Query u'abcd' eq"
" u'abcd' Unset>], valid keys: ['project', "
"'search_offset', 'timestamp', 'type', 'user']",
"'search_offset', 'severity', 'timestamp',"
" 'type', 'user']",
resp.json['error_message']['faultstring'])
def test_get_nonexistent_alarm_history(self):
@ -2182,6 +2218,7 @@ class TestAlarms(v2.FunctionalTest,
json = {
'name': 'sent_notification',
'type': 'threshold',
'severity': 'low',
'threshold_rule': {
'meter_name': 'ameter',
'comparison_operator': 'gt',

View File

@ -351,7 +351,8 @@ class TestQueryAlarmsController(tests_api.FunctionalTest,
'project_id',
'op': 'eq',
'value':
project_id}]))
project_id}]),
severity='critical')
self.alarm_conn.update_alarm(alarm)
def test_query_all(self):

View File

@ -345,7 +345,7 @@ class TestQueryToKwArgs(tests_base.BaseTestCase):
api._query_to_kwargs, q,
alarm_storage_base.Connection.get_alarm_changes)
valid_keys = ['alarm_id', 'on_behalf_of', 'project', 'search_offset',
'timestamp', 'type', 'user']
'severity', 'timestamp', 'type', 'user']
msg = ("unrecognized field in query: %s, "
"valid keys: %s") % (q, valid_keys)
expected_exc = wsme.exc.UnknownArgument('abc', msg)
@ -402,7 +402,7 @@ class TestQueryToKwArgs(tests_base.BaseTestCase):
wsme.exc.UnknownArgument,
api._query_to_kwargs, q, alarm_storage_base.Connection.get_alarms)
valid_keys = ['alarm_id', 'enabled', 'meter', 'name', 'pagination',
'project', 'state', 'type', 'user']
'project', 'severity', 'state', 'type', 'user']
msg = ("unrecognized field in query: %s, "
"valid keys: %s") % (q, valid_keys)
expected_exc = wsme.exc.UnknownArgument('abc', msg)

View File

@ -75,15 +75,15 @@ class ModelTest(testbase.BaseTestCase):
"timestamp", "user_id", "project_id", "state",
"state_timestamp", "ok_actions", "alarm_actions",
"insufficient_data_actions", "repeat_actions", "rule",
"time_constraints"]
"severity", "time_constraints"]
self.assertEqual(set(alarm_fields),
set(alarm_models.Alarm.get_field_names()))
def test_get_field_names_of_alarmchange(self):
alarmchange_fields = ["event_id", "alarm_id", "type", "detail",
"user_id", "project_id", "on_behalf_of",
"timestamp"]
"user_id", "project_id", "severity",
"on_behalf_of", "timestamp"]
self.assertEqual(set(alarmchange_fields),
set(alarm_models.AlarmChange.get_field_names()))