Provide new API endpoint for alarm state

This patch add a new endpoint to the alarm API to change only the state
of an alarm. And modifies the evaluator base to use this new endpoint.

Implements blueprint alarming-logical-combination

Change-Id: I8bff74f0686ba17103ee63bcbdb0830c8273596f
This commit is contained in:
Mehdi Abaakouk 2013-09-16 14:29:13 +02:00
parent 985f482709
commit cd96b61099
5 changed files with 71 additions and 22 deletions

View File

@ -68,7 +68,7 @@ class Evaluator(object):
'state': state,
'reason': reason})
self._client.alarms.update(alarm.alarm_id, **dict(state=state))
self._client.alarms.set_state(alarm.alarm_id, state=state)
alarm.state = state
if self.notifier:
self.notifier.notify(alarm, previous, reason)

View File

@ -70,7 +70,7 @@ ALARM_API_OPTS = [
cfg.CONF.register_opts(ALARM_API_OPTS, group='alarm')
state_kind = ["ok", "alarm", "insufficient data"]
operation_kind = wtypes.Enum(str, 'lt', 'le', 'eq', 'ne', 'ge', 'gt')
@ -1079,10 +1079,7 @@ class Alarm(_Base):
timestamp = datetime.datetime
"The date of the last alarm definition update"
#TODO(sileht): Add an explicit "set_state" operation instead of
#forcing the caller to PUT the entire definition of the alarm to test it.
#(example: POST/PUT? alarms/<alarm_id>/state)
state = AdvEnum('state', str, 'ok', 'alarm', 'insufficient data',
state = AdvEnum('state', str, *state_kind,
default='insufficient data')
"The state offset the alarm"
@ -1186,6 +1183,7 @@ class AlarmController(rest.RestController):
_custom_actions = {
'history': ['GET'],
'state': ['PUT', 'GET'],
}
def __init__(self, alarm_id):
@ -1299,6 +1297,28 @@ class AlarmController(rest.RestController):
for ac in conn.get_alarm_changes(self._id, auth_project,
**kwargs)]
@wsme_pecan.wsexpose(wtypes.text, body=wtypes.text)
def put_state(self, state):
"""Set the state of this alarm."""
if state not in state_kind:
error = _("state invalid")
pecan.response.translatable_error = error
raise wsme.exc.ClientSideError(unicode(error))
now = timeutils.utcnow()
alarm = self._alarm()
alarm.state = state
alarm.state_timestamp = now
alarm = self.conn.update_alarm(alarm)
change = {'state': alarm.state}
self._record_change(change, now, on_behalf_of=alarm.project_id,
type=storage.models.AlarmChange.STATE_TRANSITION)
return alarm.state
@wsme_pecan.wsexpose(wtypes.text)
def get_state(self):
alarm = self._alarm()
return alarm.state
class AlarmsController(rest.RestController):
"""Manages operations on the alarms collection.

View File

@ -128,7 +128,7 @@ class TestEvaluate(base.TestEvaluatorBase):
self._assert_all_alarms('insufficient data')
expected = [mock.call(alarm.alarm_id, state='insufficient data')
for alarm in self.alarms]
update_calls = self.api_client.alarms.update.call_args_list
update_calls = self.api_client.alarms.set_state.call_args_list
self.assertEqual(update_calls, expected)
expected = [mock.call(alarm,
'ok',
@ -150,7 +150,7 @@ class TestEvaluate(base.TestEvaluatorBase):
self._evaluate_all_alarms()
expected = [mock.call(alarm.alarm_id, state='ok')
for alarm in self.alarms]
update_calls = self.api_client.alarms.update.call_args_list
update_calls = self.api_client.alarms.set_state.call_args_list
self.assertEqual(update_calls, expected)
reasons = self._combination_transition_reason('ok')
expected = [mock.call(alarm, 'insufficient data', reason)
@ -170,7 +170,7 @@ class TestEvaluate(base.TestEvaluatorBase):
self._evaluate_all_alarms()
expected = [mock.call(alarm.alarm_id, state='ok')
for alarm in self.alarms]
update_calls = self.api_client.alarms.update.call_args_list
update_calls = self.api_client.alarms.set_state.call_args_list
self.assertEqual(update_calls, expected)
reasons = self._combination_transition_reason('ok')
expected = [mock.call(alarm, 'alarm', reason)
@ -190,7 +190,7 @@ class TestEvaluate(base.TestEvaluatorBase):
self._evaluate_all_alarms()
expected = [mock.call(alarm.alarm_id, state='alarm')
for alarm in self.alarms]
update_calls = self.api_client.alarms.update.call_args_list
update_calls = self.api_client.alarms.set_state.call_args_list
self.assertEqual(update_calls, expected)
reasons = self._combination_transition_reason('alarm')
expected = [mock.call(alarm, 'ok', reason)
@ -210,7 +210,7 @@ class TestEvaluate(base.TestEvaluatorBase):
self._evaluate_all_alarms()
expected = [mock.call(alarm.alarm_id, state='alarm')
for alarm in self.alarms]
update_calls = self.api_client.alarms.update.call_args_list
update_calls = self.api_client.alarms.set_state.call_args_list
self.assertEqual(update_calls, expected)
reasons = self._combination_transition_reason('alarm')
expected = [mock.call(alarm, 'ok', reason)
@ -231,7 +231,7 @@ class TestEvaluate(base.TestEvaluatorBase):
self._evaluate_all_alarms()
expected = [mock.call(alarm.alarm_id, state='insufficient data')
for alarm in self.alarms]
update_calls = self.api_client.alarms.update.call_args_list
update_calls = self.api_client.alarms.set_state.call_args_list
self.assertEqual(update_calls, expected)
reasons = ['1 alarms in'
' 9cfc3e51-2ff1-4b1d-ac01-c1bd4c6d0d1e,'
@ -256,7 +256,7 @@ class TestEvaluate(base.TestEvaluatorBase):
self._get_alarm('ok'),
]
self._evaluate_all_alarms()
update_calls = self.api_client.alarms.update.call_args_list
update_calls = self.api_client.alarms.set_state.call_args_list
self.assertEqual(update_calls, [])
self.assertEqual(self.notifier.notify.call_args_list, [])
@ -273,7 +273,7 @@ class TestEvaluate(base.TestEvaluatorBase):
self._get_alarm('ok'),
]
self._evaluate_all_alarms()
update_calls = self.api_client.alarms.update.call_args_list
update_calls = self.api_client.alarms.set_state.call_args_list
self.assertEqual(update_calls, [])
reasons = self._combination_remaining_reason('ok')
expected = [mock.call(alarm, 'ok', reason)

View File

@ -122,7 +122,7 @@ class TestEvaluate(base.TestEvaluatorBase):
self._assert_all_alarms('insufficient data')
expected = [mock.call(alarm.alarm_id, state='insufficient data')
for alarm in self.alarms]
update_calls = self.api_client.alarms.update.call_args_list
update_calls = self.api_client.alarms.set_state.call_args_list
self.assertEqual(update_calls, expected)
expected = [mock.call(alarm,
'ok',
@ -144,7 +144,7 @@ class TestEvaluate(base.TestEvaluatorBase):
self._assert_all_alarms('alarm')
expected = [mock.call(alarm.alarm_id, state='alarm')
for alarm in self.alarms]
update_calls = self.api_client.alarms.update.call_args_list
update_calls = self.api_client.alarms.set_state.call_args_list
self.assertEqual(update_calls, expected)
reasons = ['Transition to alarm due to 5 samples outside'
' threshold, most recent: 85.0',
@ -167,7 +167,7 @@ class TestEvaluate(base.TestEvaluatorBase):
self._assert_all_alarms('ok')
expected = [mock.call(alarm.alarm_id, state='ok')
for alarm in self.alarms]
update_calls = self.api_client.alarms.update.call_args_list
update_calls = self.api_client.alarms.set_state.call_args_list
self.assertEqual(update_calls, expected)
reasons = ['Transition to ok due to 5 samples inside'
' threshold, most recent: 76.0',
@ -188,7 +188,7 @@ class TestEvaluate(base.TestEvaluatorBase):
self.api_client.statistics.list.side_effect = [avgs, maxs]
self._evaluate_all_alarms()
self._assert_all_alarms('ok')
self.assertEqual(self.api_client.alarms.update.call_args_list,
self.assertEqual(self.api_client.alarms.set_state.call_args_list,
[])
self.assertEqual(self.notifier.notify.call_args_list, [])
@ -204,7 +204,7 @@ class TestEvaluate(base.TestEvaluatorBase):
self.api_client.statistics.list.side_effect = [avgs, maxs]
self._evaluate_all_alarms()
self._assert_all_alarms('ok')
self.assertEqual(self.api_client.alarms.update.call_args_list,
self.assertEqual(self.api_client.alarms.set_state.call_args_list,
[])
reason = 'Remaining as ok due to 4 samples inside' \
' threshold, most recent: 8.0'
@ -223,7 +223,7 @@ class TestEvaluate(base.TestEvaluatorBase):
self.api_client.statistics.list.side_effect = [avgs, maxs]
self._evaluate_all_alarms()
self._assert_all_alarms('alarm')
self.assertEqual(self.api_client.alarms.update.call_args_list,
self.assertEqual(self.api_client.alarms.set_state.call_args_list,
[])
reason = 'Remaining as alarm due to 4 samples outside' \
' threshold, most recent: 7.0'
@ -245,7 +245,7 @@ class TestEvaluate(base.TestEvaluatorBase):
self._assert_all_alarms('alarm')
expected = [mock.call(alarm.alarm_id, state='alarm')
for alarm in self.alarms]
update_calls = self.api_client.alarms.update.call_args_list
update_calls = self.api_client.alarms.set_state.call_args_list
self.assertEqual(update_calls, expected)
reasons = ['Transition to alarm due to 5 samples outside'
' threshold, most recent: 85.0',
@ -268,7 +268,7 @@ class TestEvaluate(base.TestEvaluatorBase):
self._assert_all_alarms('alarm')
expected = [mock.call(alarm.alarm_id, state='alarm')
for alarm in self.alarms]
update_calls = self.api_client.alarms.update.call_args_list
update_calls = self.api_client.alarms.set_state.call_args_list
self.assertEqual(update_calls, expected)
reasons = ['Transition to alarm due to 5 samples outside'
' threshold, most recent: 85.0',

View File

@ -520,6 +520,35 @@ class TestAlarms(FunctionalTest,
alarms = list(self.conn.get_alarms())
self.assertEqual(3, len(alarms))
def test_get_state_alarm(self):
data = self.get_json('/alarms')
self.assertEqual(4, len(data))
resp = self.get_json('/alarms/%s/state' % data[0]['alarm_id'],
headers=self.auth_headers)
self.assertEqual(data[0]['state'], resp)
def test_set_state_alarm(self):
data = self.get_json('/alarms')
self.assertEqual(4, len(data))
resp = self.put_json('/alarms/%s/state' % data[0]['alarm_id'],
headers=self.auth_headers,
params='alarm')
alarms = list(self.conn.get_alarms(alarm_id=data[0]['alarm_id']))
self.assertEqual(1, len(alarms))
self.assertEqual(alarms[0].state, 'alarm')
self.assertEqual(resp.json, 'alarm')
def test_set_invalid_state_alarm(self):
data = self.get_json('/alarms')
self.assertEqual(4, len(data))
self.put_json('/alarms/%s/state' % data[0]['alarm_id'],
headers=self.auth_headers,
params='not valid',
status=400)
def _get_alarm(self, id):
data = self.get_json('/alarms')
match = [a for a in data if a['alarm_id'] == id]