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):
|
def from_db_model(cls, m):
|
||||||
return cls(**(m.as_dict()))
|
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):
|
class Query(_Base):
|
||||||
"""Query filter.
|
"""Query filter.
|
||||||
@ -526,8 +536,130 @@ class ResourcesController(rest.RestController):
|
|||||||
return resources
|
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):
|
class V2Controller(object):
|
||||||
"""Version 2 API controller root."""
|
"""Version 2 API controller root."""
|
||||||
|
|
||||||
resources = ResourcesController()
|
resources = ResourcesController()
|
||||||
meters = MetersController()
|
meters = MetersController()
|
||||||
|
alarms = AlarmsController()
|
||||||
|
@ -27,6 +27,7 @@ from oslo.config import cfg
|
|||||||
import pecan
|
import pecan
|
||||||
import pecan.testing
|
import pecan.testing
|
||||||
|
|
||||||
|
from ceilometer.openstack.common import jsonutils
|
||||||
from ceilometer.api import acl
|
from ceilometer.api import acl
|
||||||
from ceilometer.api.v1 import app as v1_app
|
from ceilometer.api.v1 import app as v1_app
|
||||||
from ceilometer.api.v1 import blueprint as v1_blueprint
|
from ceilometer.api.v1 import blueprint as v1_blueprint
|
||||||
@ -130,6 +131,40 @@ class FunctionalTest(db_test_base.TestBase):
|
|||||||
super(FunctionalTest, self).tearDown()
|
super(FunctionalTest, self).tearDown()
|
||||||
pecan.set_config({}, overwrite=True)
|
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,
|
def get_json(self, path, expect_errors=False, headers=None,
|
||||||
extra_environ=None, q=[], **params):
|
extra_environ=None, q=[], **params):
|
||||||
full_path = self.PATH_PREFIX + path
|
full_path = self.PATH_PREFIX + path
|
||||||
@ -144,7 +179,7 @@ class FunctionalTest(db_test_base.TestBase):
|
|||||||
all_params.update(params)
|
all_params.update(params)
|
||||||
if q:
|
if q:
|
||||||
all_params.update(query_params)
|
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,
|
response = self.app.get(full_path,
|
||||||
params=all_params,
|
params=all_params,
|
||||||
headers=headers,
|
headers=headers,
|
||||||
@ -152,5 +187,5 @@ class FunctionalTest(db_test_base.TestBase):
|
|||||||
expect_errors=expect_errors)
|
expect_errors=expect_errors)
|
||||||
if not expect_errors:
|
if not expect_errors:
|
||||||
response = response.json
|
response = response.json
|
||||||
print 'GOT:', response
|
print('GOT:%s' % response)
|
||||||
return 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