Add just the most minimal alarm API

This is taken from Mehdi's PoC patch as a starting point.

blueprint alarm-api
Change-Id: If53a8332bdf6bd6bc727d37f5e6706db7e1f5ce8
This commit is contained in:
Angus Salkeld 2013-05-02 20:21:21 +10:00
parent 7dfd84690c
commit 0d5c2713eb
3 changed files with 238 additions and 2 deletions

View File

@ -55,6 +55,16 @@ class _Base(wtypes.Base):
def from_db_model(cls, m):
return cls(**(m.as_dict()))
def as_dict(self, db_model):
valid_keys = inspect.getargspec(db_model.__init__)[0]
if 'self' in valid_keys:
valid_keys.remove('self')
return dict((k, getattr(self, k))
for k in valid_keys
if hasattr(self, k) and
getattr(self, k) != wsme.Unset)
class Query(_Base):
"""Query filter.
@ -526,8 +536,130 @@ class ResourcesController(rest.RestController):
return resources
class Alarm(_Base):
"""One category of measurements.
"""
alarm_id = wtypes.text
"The UUID of the alarm"
name = wtypes.text
"The name for the alarm"
description = wtypes.text
"The description of the alarm"
counter_name = wtypes.text
"The name of counter"
project_id = wtypes.text
"The ID of the project or tenant that owns the alarm"
user_id = wtypes.text
"The ID of the user who created the alarm"
comparison_operator = wtypes.Enum(str, 'lt', 'le', 'eq', 'ne', 'ge', 'gt')
"The comparison against the alarm threshold"
threshold = float
"The threshold of the alarm"
statistic = wtypes.Enum(str, 'max', 'min', 'avg', 'sum', 'count')
"The statistic to compare to the threshold"
enabled = bool
"This alarm is enabled?"
evaluation_periods = int
"The number of periods to evaluate the threshold"
period = float
"The time range in seconds over which to evaluate the threshold"
timestamp = datetime.datetime
"The date of the last alarm definition update"
state = wtypes.Enum(str, 'ok', 'alarm', 'insufficient data')
"The state offset the alarm"
state_timestamp = datetime.datetime
"The date of the last alarm state changed"
ok_actions = [wtypes.text]
"The actions to do when alarm state change to ok"
alarm_actions = [wtypes.text]
"The actions to do when alarm state change to alarm"
insufficient_data_actions = [wtypes.text]
"The actions to do when alarm state change to insufficient data"
matching_metadata = {wtypes.text: wtypes.text}
"The matching_metadata of the alarm"
def __init__(self, **kwargs):
super(Alarm, self).__init__(**kwargs)
@classmethod
def sample(cls):
return cls(alarm_id=None,
name="SwiftObjectAlarm",
description="An alarm",
counter_name="storage.objects",
comparison_operator="gt",
threshold=200,
statistic="avg",
user_id="c96c887c216949acbdfbd8b494863567",
project_id="c96c887c216949acbdfbd8b494863567",
evaluation_periods=2,
period=240,
enabled=True,
timestamp=datetime.datetime.utcnow(),
state="ok",
state_timestamp=datetime.datetime.utcnow(),
ok_actions=["http://site:8000/ok"],
alarm_actions=["http://site:8000/alarm"],
insufficient_data_actions=["http://site:8000/nodata"],
matching_metadata={"key_name":
"key_value"}
)
class AlarmsController(rest.RestController):
"""Works on alarms."""
@wsme_pecan.wsexpose(Alarm, body=Alarm, status_code=201)
def post(self, data):
"""Create a new alarm"""
raise wsme.exc.ClientSideError("Not implemented")
@wsme_pecan.wsexpose(Alarm, wtypes.text, body=Alarm, status_code=201)
def put(self, alarm_id, data):
"""Modify an alarm"""
raise wsme.exc.ClientSideError("Not implemented")
@wsme_pecan.wsexpose(None, wtypes.text, status_code=204)
def delete(self, alarm_id):
"""Delete an alarm"""
raise wsme.exc.ClientSideError("Not implemented")
@wsme_pecan.wsexpose(Alarm, wtypes.text)
def get_one(self, alarm_id):
"""Return one alarm"""
raise wsme.exc.ClientSideError("Not implemented")
@wsme_pecan.wsexpose([Alarm], [Query])
def get_all(self, q=[]):
"""Return all alarms, based on the query provided.
:param q: Filter rules for the alarms to be returned.
"""
return []
class V2Controller(object):
"""Version 2 API controller root."""
resources = ResourcesController()
meters = MetersController()
alarms = AlarmsController()

View File

@ -27,6 +27,7 @@ from oslo.config import cfg
import pecan
import pecan.testing
from ceilometer.openstack.common import jsonutils
from ceilometer.api import acl
from ceilometer.api.v1 import app as v1_app
from ceilometer.api.v1 import blueprint as v1_blueprint
@ -130,6 +131,40 @@ class FunctionalTest(db_test_base.TestBase):
super(FunctionalTest, self).tearDown()
pecan.set_config({}, overwrite=True)
def put_json(self, path, params, expect_errors=False, headers=None,
extra_environ=None, status=None):
return self.post_json(path=path, params=params,
expect_errors=expect_errors,
headers=headers, extra_environ=extra_environ,
status=status, method="put")
def post_json(self, path, params, expect_errors=False, headers=None,
method="post", extra_environ=None, status=None):
full_path = self.PATH_PREFIX + path
print('%s: %s %s' % (method.upper(), full_path, params))
response = getattr(self.app, "%s_json" % method)(
full_path,
params=params,
headers=headers,
status=status,
extra_environ=extra_environ,
expect_errors=expect_errors
)
print('GOT:%s' % response)
return response
def delete(self, path, expect_errors=False, headers=None,
extra_environ=None, status=None):
full_path = self.PATH_PREFIX + path
print('DELETE: %s' % (full_path))
response = self.app.delete(full_path,
headers=headers,
status=status,
extra_environ=extra_environ,
expect_errors=expect_errors)
print('GOT:%s' % response)
return response
def get_json(self, path, expect_errors=False, headers=None,
extra_environ=None, q=[], **params):
full_path = self.PATH_PREFIX + path
@ -144,7 +179,7 @@ class FunctionalTest(db_test_base.TestBase):
all_params.update(params)
if q:
all_params.update(query_params)
print 'GET: %s %r' % (full_path, all_params)
print('GET: %s %r' % (full_path, all_params))
response = self.app.get(full_path,
params=all_params,
headers=headers,
@ -152,5 +187,5 @@ class FunctionalTest(db_test_base.TestBase):
expect_errors=expect_errors)
if not expect_errors:
response = response.json
print 'GOT:', response
print('GOT:%s' % response)
return response

View File

@ -0,0 +1,69 @@
# -*- encoding: utf-8 -*-
#
# Copyright © 2013 eNovance <licensing@enovance.com>
#
# Author: Mehdi Abaakouk <mehdi.abaakouk@enovance.com>
# Angus Salkeld <asalkeld@redhat.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.
'''Tests alarm operation
'''
import logging
from .base import FunctionalTest
LOG = logging.getLogger(__name__)
class TestListEmptyAlarms(FunctionalTest):
def test_empty(self):
data = self.get_json('/alarms')
self.assertEquals([], data)
class TestAlarms(FunctionalTest):
def setUp(self):
super(TestAlarms, self).setUp()
def test_list_alarms(self):
data = self.get_json('/alarms')
self.assertEquals(0, len(data))
def test_get_alarm(self):
data = self.get_json('/alarms/1', expect_errors=True)
self.assertEquals(data.status_int, 400)
def test_post_alarm(self):
json = {
'name': 'added_alarm',
'counter_name': 'ameter',
'comparison_operator': 'gt',
'threshold': 2.0,
'statistic': 'avg',
}
data = self.post_json('/alarms', params=json, expect_errors=True)
self.assertEquals(data.status_int, 400)
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)
def test_delete_alarm(self):
data = self.delete('/alarms/1', expect_errors=True)
self.assertEquals(data.status_int, 400)