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:
parent
7dfd84690c
commit
0d5c2713eb
@ -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()
|
||||
|
@ -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
|
||||
|
69
tests/api/v2/test_alarm.py
Normal file
69
tests/api/v2/test_alarm.py
Normal 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)
|
Loading…
Reference in New Issue
Block a user