Add event paging
Change-Id: Ifaa82db925ca954fea19a91de43f07ff15f8ebd8
This commit is contained in:
parent
cd907a7622
commit
3d09835ea7
surveil
api/handlers/status
tests/api/controllers/v2/status
@ -23,43 +23,6 @@ class EventHandler(handler.Handler):
|
|||||||
def get_all(self, live_query=None):
|
def get_all(self, live_query=None):
|
||||||
"""Return all logs."""
|
"""Return all logs."""
|
||||||
influx_client = self.request.influxdb_client
|
influx_client = self.request.influxdb_client
|
||||||
query = influxdb_query.build_influxdb_query(live_query, "EVENT")
|
query = influxdb_query.build_influxdb_query(live_query, "EVENT",
|
||||||
response = influx_client.query(query)
|
multiple_series=True)
|
||||||
|
return influxdb_query.paging(influx_client.query(query), event.Event, live_query)
|
||||||
events = []
|
|
||||||
|
|
||||||
for item in response.items():
|
|
||||||
tags = item[0][1]
|
|
||||||
for point in response.get_points(tags=tags):
|
|
||||||
point.update(tags)
|
|
||||||
event_dict = self._event_dict_from_influx_item(point)
|
|
||||||
events.append(event.Event(**event_dict))
|
|
||||||
|
|
||||||
return events
|
|
||||||
|
|
||||||
def _event_dict_from_influx_item(self, item):
|
|
||||||
mappings = [
|
|
||||||
'time',
|
|
||||||
'event_type',
|
|
||||||
'host_name',
|
|
||||||
'service_description',
|
|
||||||
'state',
|
|
||||||
'state_type',
|
|
||||||
'attempts',
|
|
||||||
'downtime_type',
|
|
||||||
'notification_type',
|
|
||||||
'notification_method',
|
|
||||||
'contact',
|
|
||||||
'alert_type',
|
|
||||||
'output',
|
|
||||||
'acknowledgement'
|
|
||||||
]
|
|
||||||
|
|
||||||
event_dict = {}
|
|
||||||
|
|
||||||
for field in mappings:
|
|
||||||
value = item.get(field, None)
|
|
||||||
if value is not None and value != "":
|
|
||||||
event_dict[field] = value
|
|
||||||
|
|
||||||
return event_dict
|
|
@ -12,6 +12,7 @@
|
|||||||
# License for the specific language governing permissions and limitations
|
# License for the specific language governing permissions and limitations
|
||||||
# under the License.
|
# under the License.
|
||||||
|
|
||||||
|
import functools
|
||||||
import json
|
import json
|
||||||
|
|
||||||
|
|
||||||
@ -20,8 +21,8 @@ def build_influxdb_query(live_query,
|
|||||||
group_by=[],
|
group_by=[],
|
||||||
order_by=[],
|
order_by=[],
|
||||||
additional_filters={},
|
additional_filters={},
|
||||||
limit=None):
|
limit=None,
|
||||||
|
multiple_series=False):
|
||||||
query = ['SELECT * FROM', measurement]
|
query = ['SELECT * FROM', measurement]
|
||||||
|
|
||||||
filters = {}
|
filters = {}
|
||||||
@ -33,8 +34,11 @@ def build_influxdb_query(live_query,
|
|||||||
if live_query.time_interval:
|
if live_query.time_interval:
|
||||||
time = live_query.time_interval
|
time = live_query.time_interval
|
||||||
if live_query.paging:
|
if live_query.paging:
|
||||||
limit = live_query.paging.size
|
if multiple_series:
|
||||||
offset = limit * live_query.paging.page
|
limit = live_query.paging.size * (live_query.paging.page + 1)
|
||||||
|
else:
|
||||||
|
limit = live_query.paging.size
|
||||||
|
offset = (live_query.paging.page + 1) * live_query.paging.size
|
||||||
|
|
||||||
filters.update(additional_filters)
|
filters.update(additional_filters)
|
||||||
query += _build_where_clause(filters, time)
|
query += _build_where_clause(filters, time)
|
||||||
@ -90,3 +94,66 @@ def _build_where_clause(filters, time=None):
|
|||||||
value))
|
value))
|
||||||
|
|
||||||
return clause
|
return clause
|
||||||
|
|
||||||
|
|
||||||
|
def paging(response, datamodel, live_query=None):
|
||||||
|
"""Paging function
|
||||||
|
|
||||||
|
:param response: a python-influxdb resulset
|
||||||
|
:param datamodel: an Surveil API datamodel class
|
||||||
|
:param live_query: an influxdb_query
|
||||||
|
:return: a dict of datamodel object. If the live query contain paging,
|
||||||
|
the dict is sorted by datamodel time attribute and contain
|
||||||
|
live_query.paging.size object for the live_query.paging.page page
|
||||||
|
"""
|
||||||
|
if live_query and live_query.paging:
|
||||||
|
limit_paging = live_query.paging.size * (live_query.paging.page + 1)
|
||||||
|
limit = live_query.paging.size + live_query.paging.page
|
||||||
|
offset_paging = live_query.paging.page * live_query.paging.size
|
||||||
|
|
||||||
|
def sort_by_time(init, point_tag):
|
||||||
|
event = {}
|
||||||
|
event.update(point_tag[0])
|
||||||
|
event.update(point_tag[1])
|
||||||
|
init.append(datamodel(**event))
|
||||||
|
init.sort(key=lambda event: event.time,
|
||||||
|
reverse=True)
|
||||||
|
return init[:limit_paging]
|
||||||
|
|
||||||
|
response = [(tag[1], _dict_from_influx_item(datamodel, point))
|
||||||
|
for tag, points in response.items()
|
||||||
|
for point in points]
|
||||||
|
|
||||||
|
event_list = functools.reduce(sort_by_time, response, [])
|
||||||
|
|
||||||
|
return event_list[offset_paging:limit+1]
|
||||||
|
|
||||||
|
else:
|
||||||
|
events = []
|
||||||
|
|
||||||
|
for item in response.items():
|
||||||
|
tags = item[0][1]
|
||||||
|
for point in response.get_points(tags=tags):
|
||||||
|
point.update(tags)
|
||||||
|
event_dict = _dict_from_influx_item(datamodel, point)
|
||||||
|
events.append(datamodel(**event_dict))
|
||||||
|
|
||||||
|
return events
|
||||||
|
|
||||||
|
|
||||||
|
def _dict_from_influx_item(datamodel, item):
|
||||||
|
"""Create a dict representing a python-influxdb item
|
||||||
|
|
||||||
|
:param item: an python influxdb item object
|
||||||
|
:param datamodel: an Surveil API datamodel class
|
||||||
|
:return: a dict (datamodel_attribute:item_value)
|
||||||
|
|
||||||
|
>>> _event_dict_from_influx_item(Event, {"time": 4})
|
||||||
|
{'time': 4}
|
||||||
|
>>> _event_dict_from_influx_item(Event, {"time": "null"})
|
||||||
|
{}
|
||||||
|
"""
|
||||||
|
|
||||||
|
fields = [attr.name for attr in getattr(datamodel, "_wsme_attributes")]
|
||||||
|
return dict([(field, item.get(field, None)) for field in fields
|
||||||
|
if item.get(field, None) is not None])
|
||||||
|
@ -44,7 +44,7 @@ class TestEvents(functionalTest.FunctionalTest):
|
|||||||
],
|
],
|
||||||
"values": [
|
"values": [
|
||||||
[
|
[
|
||||||
"2015-06-04T18:55:12Z",
|
"2015-06-04T18:55:19Z",
|
||||||
1,
|
1,
|
||||||
"Connection refused",
|
"Connection refused",
|
||||||
"CRITICAL",
|
"CRITICAL",
|
||||||
@ -52,7 +52,7 @@ class TestEvents(functionalTest.FunctionalTest):
|
|||||||
"SERVICE"
|
"SERVICE"
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
'2015-06-04T18:55:12Z',
|
'2015-06-04T18:55:18Z',
|
||||||
2,
|
2,
|
||||||
'Connection refused',
|
'Connection refused',
|
||||||
'CRITICAL',
|
'CRITICAL',
|
||||||
@ -60,7 +60,7 @@ class TestEvents(functionalTest.FunctionalTest):
|
|||||||
'SERVICE'
|
'SERVICE'
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
'2015-06-04T18:55:12Z',
|
'2015-06-04T18:55:17Z',
|
||||||
3,
|
3,
|
||||||
'Connection refused',
|
'Connection refused',
|
||||||
'CRITICAL',
|
'CRITICAL',
|
||||||
@ -86,7 +86,7 @@ class TestEvents(functionalTest.FunctionalTest):
|
|||||||
],
|
],
|
||||||
'values': [
|
'values': [
|
||||||
[
|
[
|
||||||
'2015-06-04T18:55:12Z',
|
'2015-06-04T18:55:16Z',
|
||||||
1,
|
1,
|
||||||
'Warning - Connection refused',
|
'Warning - Connection refused',
|
||||||
'CRITICAL',
|
'CRITICAL',
|
||||||
@ -94,7 +94,7 @@ class TestEvents(functionalTest.FunctionalTest):
|
|||||||
'SERVICE'
|
'SERVICE'
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
'2015-06-04T18:55:12Z',
|
'2015-06-04T18:55:15Z',
|
||||||
2,
|
2,
|
||||||
'Warning - Connection refused',
|
'Warning - Connection refused',
|
||||||
'WARNING',
|
'WARNING',
|
||||||
@ -120,7 +120,7 @@ class TestEvents(functionalTest.FunctionalTest):
|
|||||||
],
|
],
|
||||||
'values': [
|
'values': [
|
||||||
[
|
[
|
||||||
'2015-06-04T18:55:12Z',
|
'2015-06-04T18:55:14Z',
|
||||||
'SERVICE',
|
'SERVICE',
|
||||||
'admin',
|
'admin',
|
||||||
'CRITICAL',
|
'CRITICAL',
|
||||||
@ -128,7 +128,7 @@ class TestEvents(functionalTest.FunctionalTest):
|
|||||||
None
|
None
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
'2015-06-04T18:55:12Z',
|
'2015-06-04T18:55:13Z',
|
||||||
'SERVICE',
|
'SERVICE',
|
||||||
'admin',
|
'admin',
|
||||||
'CRITICAL',
|
'CRITICAL',
|
||||||
@ -174,7 +174,7 @@ class TestEvents(functionalTest.FunctionalTest):
|
|||||||
"host_name": "myServiceIsDown",
|
"host_name": "myServiceIsDown",
|
||||||
"event_type": "ALERT",
|
"event_type": "ALERT",
|
||||||
"service_description": "iAmADownService",
|
"service_description": "iAmADownService",
|
||||||
"time": "2015-06-04T18:55:12Z",
|
"time": "2015-06-04T18:55:19Z",
|
||||||
"attempts": 1,
|
"attempts": 1,
|
||||||
"output": "Connection refused",
|
"output": "Connection refused",
|
||||||
"state": "CRITICAL",
|
"state": "CRITICAL",
|
||||||
@ -185,7 +185,7 @@ class TestEvents(functionalTest.FunctionalTest):
|
|||||||
'host_name': 'myServiceIsDown',
|
'host_name': 'myServiceIsDown',
|
||||||
'event_type': 'ALERT',
|
'event_type': 'ALERT',
|
||||||
'service_description': 'iAmADownService',
|
'service_description': 'iAmADownService',
|
||||||
'time': '2015-06-04T18:55:12Z',
|
'time': '2015-06-04T18:55:18Z',
|
||||||
'attempts': 2,
|
'attempts': 2,
|
||||||
'output': 'Connection refused',
|
'output': 'Connection refused',
|
||||||
'state': 'CRITICAL',
|
'state': 'CRITICAL',
|
||||||
@ -196,7 +196,7 @@ class TestEvents(functionalTest.FunctionalTest):
|
|||||||
'host_name': 'myServiceIsDown',
|
'host_name': 'myServiceIsDown',
|
||||||
'event_type': 'ALERT',
|
'event_type': 'ALERT',
|
||||||
'service_description': 'iAmADownService',
|
'service_description': 'iAmADownService',
|
||||||
'time': '2015-06-04T18:55:12Z',
|
'time': '2015-06-04T18:55:17Z',
|
||||||
'attempts': 3,
|
'attempts': 3,
|
||||||
'output': 'Connection refused',
|
'output': 'Connection refused',
|
||||||
'state': 'CRITICAL',
|
'state': 'CRITICAL',
|
||||||
@ -207,7 +207,7 @@ class TestEvents(functionalTest.FunctionalTest):
|
|||||||
'host_name': 'savoirfairelinux',
|
'host_name': 'savoirfairelinux',
|
||||||
'event_type': 'ALERT',
|
'event_type': 'ALERT',
|
||||||
'service_description': 'CPU',
|
'service_description': 'CPU',
|
||||||
'time': '2015-06-04T18:55:12Z',
|
'time': '2015-06-04T18:55:16Z',
|
||||||
'attempts': 1,
|
'attempts': 1,
|
||||||
'output': 'Warning - Connection refused',
|
'output': 'Warning - Connection refused',
|
||||||
'state': 'CRITICAL',
|
'state': 'CRITICAL',
|
||||||
@ -218,7 +218,7 @@ class TestEvents(functionalTest.FunctionalTest):
|
|||||||
'host_name': 'savoirfairelinux',
|
'host_name': 'savoirfairelinux',
|
||||||
'event_type': 'ALERT',
|
'event_type': 'ALERT',
|
||||||
'service_description': 'CPU',
|
'service_description': 'CPU',
|
||||||
'time': '2015-06-04T18:55:12Z',
|
'time': '2015-06-04T18:55:15Z',
|
||||||
'attempts': 2,
|
'attempts': 2,
|
||||||
'output': 'Warning - Connection refused',
|
'output': 'Warning - Connection refused',
|
||||||
'state': 'WARNING',
|
'state': 'WARNING',
|
||||||
@ -229,7 +229,7 @@ class TestEvents(functionalTest.FunctionalTest):
|
|||||||
'host_name': 'savoirfairelinux',
|
'host_name': 'savoirfairelinux',
|
||||||
'event_type': 'NOTIFICATION',
|
'event_type': 'NOTIFICATION',
|
||||||
'service_description': 'CPU',
|
'service_description': 'CPU',
|
||||||
'time': '2015-06-04T18:55:12Z',
|
'time': '2015-06-04T18:55:14Z',
|
||||||
'notification_type': 'SERVICE',
|
'notification_type': 'SERVICE',
|
||||||
'contact': 'admin',
|
'contact': 'admin',
|
||||||
'state': 'CRITICAL',
|
'state': 'CRITICAL',
|
||||||
@ -239,7 +239,7 @@ class TestEvents(functionalTest.FunctionalTest):
|
|||||||
'host_name': 'savoirfairelinux',
|
'host_name': 'savoirfairelinux',
|
||||||
'event_type': 'NOTIFICATION',
|
'event_type': 'NOTIFICATION',
|
||||||
'service_description': 'CPU',
|
'service_description': 'CPU',
|
||||||
'time': '2015-06-04T18:55:12Z',
|
'time': '2015-06-04T18:55:13Z',
|
||||||
'notification_type': 'SERVICE',
|
'notification_type': 'SERVICE',
|
||||||
'contact': 'admin',
|
'contact': 'admin',
|
||||||
'state': 'CRITICAL',
|
'state': 'CRITICAL',
|
||||||
@ -344,3 +344,45 @@ class TestEvents(functionalTest.FunctionalTest):
|
|||||||
'notification_method': 'notify-service-by-email'
|
'notification_method': 'notify-service-by-email'
|
||||||
}]
|
}]
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def test_paging(self):
|
||||||
|
with requests_mock.Mocker() as m:
|
||||||
|
m.register_uri(requests_mock.GET,
|
||||||
|
'http://influxdb:8086/query',
|
||||||
|
text=self.influxdb_response)
|
||||||
|
|
||||||
|
query = {'paging': {'page': 1, 'size': 2}}
|
||||||
|
|
||||||
|
response = self.post_json('/v2/status/events', params=query)
|
||||||
|
|
||||||
|
self.assertEqual(
|
||||||
|
m.last_request.qs['q'],
|
||||||
|
["select * from event limit 4"]
|
||||||
|
)
|
||||||
|
|
||||||
|
self.assert_count_equal_backport(
|
||||||
|
json.loads(response.body.decode()), [
|
||||||
|
|
||||||
|
{
|
||||||
|
'host_name': 'myServiceIsDown',
|
||||||
|
'event_type': 'ALERT',
|
||||||
|
'service_description': 'iAmADownService',
|
||||||
|
'time': '2015-06-04T18:55:17Z',
|
||||||
|
'attempts': 3,
|
||||||
|
'output': 'Connection refused',
|
||||||
|
'state': 'CRITICAL',
|
||||||
|
'state_type': 'SOFT',
|
||||||
|
'alert_type': 'SERVICE'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'host_name': 'savoirfairelinux',
|
||||||
|
'event_type': 'ALERT',
|
||||||
|
'service_description': 'CPU',
|
||||||
|
'time': '2015-06-04T18:55:16Z',
|
||||||
|
'attempts': 1,
|
||||||
|
'output': 'Warning - Connection refused',
|
||||||
|
'state': 'CRITICAL',
|
||||||
|
'state_type': 'HARD',
|
||||||
|
'alert_type': 'SERVICE'
|
||||||
|
}]
|
||||||
|
)
|
@ -4,7 +4,7 @@
|
|||||||
# not use this file except in compliance with the License. You may obtain
|
# not use this file except in compliance with the License. You may obtain
|
||||||
# a copy of the License at
|
# a copy of the License at
|
||||||
#
|
#
|
||||||
# http://www.apache.org/licenses/LICENSE-2.0
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
#
|
#
|
||||||
# Unless required by applicable law or agreed to in writing, software
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
@ -230,7 +230,7 @@ class TestHostMetric(functionalTest.FunctionalTest):
|
|||||||
"and host_name='srv-monitoring-01' "
|
"and host_name='srv-monitoring-01' "
|
||||||
"and service_description='load' "
|
"and service_description='load' "
|
||||||
"order by time desc "
|
"order by time desc "
|
||||||
"limit 10 offset 30"
|
"limit 10 offset 40"
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
self.assert_count_equal_backport(
|
self.assert_count_equal_backport(
|
||||||
|
Loading…
x
Reference in New Issue
Block a user