diff --git a/ceilometer/api/controllers/v2.py b/ceilometer/api/controllers/v2.py index b2a46b331..f5a213734 100644 --- a/ceilometer/api/controllers/v2.py +++ b/ceilometer/api/controllers/v2.py @@ -684,25 +684,81 @@ class Alarm(_Base): class AlarmsController(rest.RestController): """Works on alarms.""" + @wsme.validate(Alarm) @wsme_pecan.wsexpose(Alarm, body=Alarm, status_code=201) def post(self, data): """Create a new alarm""" - raise wsme.exc.ClientSideError("Not implemented") + conn = pecan.request.storage_conn - @wsme_pecan.wsexpose(Alarm, wtypes.text, body=Alarm, status_code=201) + data.user_id = pecan.request.headers.get('X-User-Id') + data.project_id = pecan.request.headers.get('X-Project-Id') + data.alarm_id = wsme.Unset + data.state_timestamp = wsme.Unset + data.timestamp = timeutils.utcnow() + + # make sure alarms are unique by name per project. + alarms = list(conn.get_alarms(name=data.name, + project=data.project_id)) + if len(alarms) > 0: + raise wsme.exc.ClientSideError(_("Alarm with that name exists")) + + try: + kwargs = data.as_dict(storage.models.Alarm) + alarm_in = storage.models.Alarm(**kwargs) + except Exception as ex: + LOG.exception(ex) + raise wsme.exc.ClientSideError(_("Alarm incorrect")) + + alarm = conn.update_alarm(alarm_in) + return Alarm.from_db_model(alarm) + + @wsme.validate(Alarm) + @wsme_pecan.wsexpose(Alarm, wtypes.text, body=Alarm) def put(self, alarm_id, data): """Modify an alarm""" - raise wsme.exc.ClientSideError("Not implemented") + conn = pecan.request.storage_conn + data.state_timestamp = wsme.Unset + data.alarm_id = alarm_id + data.user_id = pecan.request.headers.get('X-User-Id') + data.project_id = pecan.request.headers.get('X-Project-Id') + + alarms = list(conn.get_alarms(alarm_id=alarm_id, + project=data.project_id)) + if len(alarms) < 1: + raise wsme.exc.ClientSideError(_("Unknown alarm")) + + # merge the new values from kwargs into the current + # alarm "alarm_in". + alarm_in = alarms[0] + kwargs = data.as_dict(storage.models.Alarm) + for k, v in kwargs.iteritems(): + setattr(alarm_in, k, v) + if k == 'state': + alarm_in.state_timestamp = timeutils.utcnow() + + alarm = conn.update_alarm(alarm_in) + return Alarm.from_db_model(alarm) @wsme_pecan.wsexpose(None, wtypes.text, status_code=204) def delete(self, alarm_id): """Delete an alarm""" - raise wsme.exc.ClientSideError("Not implemented") + conn = pecan.request.storage_conn + project_id = pecan.request.headers.get('X-Project-Id') + alarms = list(conn.get_alarms(alarm_id=alarm_id, + project=project_id)) + if len(alarms) < 1: + raise wsme.exc.ClientSideError(_("Unknown alarm")) + + conn.delete_alarm(alarm_id) @wsme_pecan.wsexpose(Alarm, wtypes.text) def get_one(self, alarm_id): """Return one alarm""" - raise wsme.exc.ClientSideError("Not implemented") + alarms = list(pecan.request.storage_conn.get_alarms(alarm_id=alarm_id)) + if len(alarms) < 1: + raise wsme.exc.ClientSideError(_("Unknown alarm")) + + return Alarm.from_db_model(alarms[0]) @wsme_pecan.wsexpose([Alarm], [Query]) def get_all(self, q=[]): @@ -710,7 +766,10 @@ class AlarmsController(rest.RestController): :param q: Filter rules for the alarms to be returned. """ - return [] + kwargs = _query_to_kwargs(q, + pecan.request.storage_conn.get_alarms) + return [Alarm.from_db_model(m) + for m in pecan.request.storage_conn.get_alarms(**kwargs)] class V2Controller(object): diff --git a/tests/api/v2/test_alarm.py b/tests/api/v2/test_alarm.py index 52a12a207..9c323225a 100644 --- a/tests/api/v2/test_alarm.py +++ b/tests/api/v2/test_alarm.py @@ -20,9 +20,12 @@ ''' import logging +import uuid from .base import FunctionalTest +from ceilometer.storage.models import Alarm + LOG = logging.getLogger(__name__) @@ -38,13 +41,58 @@ class TestAlarms(FunctionalTest): def setUp(self): super(TestAlarms, self).setUp() + self.auth_headers = {'X-User-Id': str(uuid.uuid1()), + 'X-Project-Id': str(uuid.uuid1())} + + for alarm in [Alarm(alarm_id='1', name='name1', + counter_name='meter.test', + comparison_operator='gt', threshold=2.0, + statistic='avg', + user_id=self.auth_headers['X-User-Id'], + project_id=self.auth_headers['X-Project-Id']), + Alarm(alarm_id='2', name='name2', + counter_name='meter.mine', + comparison_operator='gt', threshold=2.0, + statistic='avg', + user_id=self.auth_headers['X-User-Id'], + project_id=self.auth_headers['X-Project-Id']), + Alarm(alarm_id='3', name='name3', + counter_name='meter.test', + comparison_operator='gt', threshold=2.0, + statistic='avg', + user_id=self.auth_headers['X-User-Id'], + project_id=self.auth_headers['X-Project-Id']), + ]: + self.conn.update_alarm(alarm) + def test_list_alarms(self): data = self.get_json('/alarms') - self.assertEquals(0, len(data)) + self.assertEquals(3, len(data)) + self.assertEquals(set(r['name'] for r in data), + set(['name1', + 'name2', + 'name3'])) + self.assertEquals(set(r['counter_name'] for r in data), + set(['meter.test', + 'meter.mine'])) def test_get_alarm(self): - data = self.get_json('/alarms/1', expect_errors=True) - self.assertEquals(data.status_int, 400) + data = self.get_json('/alarms/1') + self.assertEquals(data['name'], 'name1') + self.assertEquals(data['counter_name'], 'meter.test') + + def test_post_invalid_alarm(self): + json = { + 'name': 'added_alarm', + 'counter_name': 'ameter', + 'comparison_operator': 'gt', + 'threshold': 2.0, + 'statistic': 'magic', + } + self.post_json('/alarms', params=json, expect_errors=True, status=400, + headers=self.auth_headers) + alarms = list(self.conn.get_alarms()) + self.assertEquals(3, len(alarms)) def test_post_alarm(self): json = { @@ -54,16 +102,38 @@ class TestAlarms(FunctionalTest): 'threshold': 2.0, 'statistic': 'avg', } - data = self.post_json('/alarms', params=json, expect_errors=True) - self.assertEquals(data.status_int, 400) + self.post_json('/alarms', params=json, status=200, + headers=self.auth_headers) + alarms = list(self.conn.get_alarms()) + self.assertEquals(4, len(alarms)) def test_put_alarm(self): json = { 'name': 'renamed_alarm', } - data = self.put_json('/alarms/1', params=json, expect_errors=True) - self.assertEquals(data.status_int, 400) + self.put_json('/alarms/1', params=json, + headers=self.auth_headers) + alarm = list(self.conn.get_alarms(alarm_id='1'))[0] + self.assertEquals(alarm.name, json['name']) + + def test_put_alarm_wrong_field(self): + ''' + Note: wsme will ignore unknown fields so will + just not appear in the Alarm. + ''' + json = { + 'name': 'renamed_alarm', + 'this_can_not_be_correct': 'ha', + } + resp = self.put_json('/alarms/1', params=json, + expect_errors=True, + headers=self.auth_headers) + self.assertEquals(resp.status_code, 200) def test_delete_alarm(self): - data = self.delete('/alarms/1', expect_errors=True) - self.assertEquals(data.status_int, 400) + data = self.get_json('/alarms') + self.assertEquals(3, len(data)) + + self.delete('/alarms/1', status=200) + alarms = list(self.conn.get_alarms()) + self.assertEquals(2, len(alarms))