Status API: Return live hosts and services
Change-Id: I97ccadfa06387924a368a5ac2fb27ba374b7f03f
This commit is contained in:
parent
26efcedf6b
commit
fa47ae8302
@ -17,7 +17,7 @@ Hosts
|
|||||||
.. rest-controller:: surveil.api.controllers.v2.status.hosts:HostController
|
.. rest-controller:: surveil.api.controllers.v2.status.hosts:HostController
|
||||||
:webprefix: /v2/status/hosts/
|
:webprefix: /v2/status/hosts/
|
||||||
|
|
||||||
.. rest-controller:: surveil.api.controllers.v2.status.hosts.config:ConfigController
|
.. rest-controller:: surveil.api.controllers.v2.status.hosts:ConfigController
|
||||||
:webprefix: /v2/status/hosts/(host_name)/config
|
:webprefix: /v2/status/hosts/(host_name)/config
|
||||||
|
|
||||||
.. rest-controller:: surveil.api.controllers.v2.status.metrics:MetricsController
|
.. rest-controller:: surveil.api.controllers.v2.status.metrics:MetricsController
|
||||||
@ -38,6 +38,13 @@ Hosts
|
|||||||
.. rest-controller:: surveil.api.controllers.v2.logs.notifications:NotificationsController
|
.. rest-controller:: surveil.api.controllers.v2.logs.notifications:NotificationsController
|
||||||
:webprefix: /v2/status/hosts/(host_name)/events/notifications
|
:webprefix: /v2/status/hosts/(host_name)/events/notifications
|
||||||
|
|
||||||
|
Services
|
||||||
|
========
|
||||||
|
|
||||||
|
.. rest-controller:: surveil.api.controllers.v2.status.services:ServicesController
|
||||||
|
:webprefix: /v2/status/services
|
||||||
|
|
||||||
|
|
||||||
Metrics
|
Metrics
|
||||||
=======
|
=======
|
||||||
|
|
||||||
@ -46,3 +53,15 @@ Metrics
|
|||||||
|
|
||||||
.. rest-controller:: surveil.api.controllers.v2.status.metrics:MetricController
|
.. rest-controller:: surveil.api.controllers.v2.status.metrics:MetricController
|
||||||
:webprefix: /v2/status/metrics/
|
:webprefix: /v2/status/metrics/
|
||||||
|
|
||||||
|
Types
|
||||||
|
=====
|
||||||
|
|
||||||
|
.. autotype:: surveil.api.datamodel.status.live_service.LiveService
|
||||||
|
:members:
|
||||||
|
|
||||||
|
.. autotype:: surveil.api.datamodel.status.live_host.LiveHost
|
||||||
|
:members:
|
||||||
|
|
||||||
|
.. autotype:: surveil.api.datamodel.status.live_query.LiveQuery
|
||||||
|
:members:
|
||||||
|
@ -8,3 +8,4 @@ oslo.middleware
|
|||||||
oslo.policy>=0.3.0
|
oslo.policy>=0.3.0
|
||||||
keystonemiddleware
|
keystonemiddleware
|
||||||
PasteDeploy
|
PasteDeploy
|
||||||
|
influxdb==2.0.1
|
||||||
|
@ -26,13 +26,15 @@ server = {
|
|||||||
# as long as is it in the same format.
|
# as long as is it in the same format.
|
||||||
surveil_api_config = {
|
surveil_api_config = {
|
||||||
"mongodb_uri": "mongodb://mongo:27017",
|
"mongodb_uri": "mongodb://mongo:27017",
|
||||||
"ws_arbiter_url": "http://shinken:7760"
|
"ws_arbiter_url": "http://shinken:7760",
|
||||||
|
"influxdb_uri": "influxdb://root:root@influxdb:8086/db"
|
||||||
}
|
}
|
||||||
|
|
||||||
app_hooks = [
|
app_hooks = [
|
||||||
hooks.DBHook(
|
hooks.DBHook(
|
||||||
surveil_api_config['mongodb_uri'],
|
surveil_api_config['mongodb_uri'],
|
||||||
surveil_api_config['ws_arbiter_url']
|
surveil_api_config['ws_arbiter_url'],
|
||||||
|
surveil_api_config['influxdb_uri']
|
||||||
)
|
)
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -16,10 +16,11 @@ from pecan import rest
|
|||||||
|
|
||||||
from surveil.api.controllers.v2.status import hosts as v2_hosts
|
from surveil.api.controllers.v2.status import hosts as v2_hosts
|
||||||
from surveil.api.controllers.v2.status import metrics
|
from surveil.api.controllers.v2.status import metrics
|
||||||
|
from surveil.api.controllers.v2.status import services as v2_services
|
||||||
|
|
||||||
|
|
||||||
class StatusController(rest.RestController):
|
class StatusController(rest.RestController):
|
||||||
# events = EventsController()
|
# events = EventsController()
|
||||||
hosts = v2_hosts.HostsController()
|
hosts = v2_hosts.HostsController()
|
||||||
# services = ServicesController()
|
services = v2_services.ServicesController()
|
||||||
metrics = metrics.MetricsController()
|
metrics = metrics.MetricsController()
|
||||||
|
@ -14,18 +14,30 @@
|
|||||||
|
|
||||||
import pecan
|
import pecan
|
||||||
from pecan import rest
|
from pecan import rest
|
||||||
|
import wsmeext.pecan as wsme_pecan
|
||||||
|
|
||||||
from surveil.api.controllers.v2 import logs
|
from surveil.api.controllers.v2 import logs
|
||||||
from surveil.api.controllers.v2.status.hosts import config
|
|
||||||
from surveil.api.controllers.v2.status import metrics
|
from surveil.api.controllers.v2.status import metrics
|
||||||
|
from surveil.api.datamodel.status import live_host
|
||||||
|
from surveil.api.datamodel.status import live_query
|
||||||
|
from surveil.api.handlers.status import live_host_handler
|
||||||
|
|
||||||
|
|
||||||
class HostsController(rest.RestController):
|
class HostsController(rest.RestController):
|
||||||
|
|
||||||
@pecan.expose()
|
@wsme_pecan.wsexpose([live_host.LiveHost])
|
||||||
def get_all(self):
|
def get_all(self):
|
||||||
"""Returns all hosts."""
|
"""Returns all hosts."""
|
||||||
return "ALLL HOSSSSSSSST"
|
handler = live_host_handler.HostHandler(pecan.request)
|
||||||
|
hosts = handler.get_all()
|
||||||
|
return hosts
|
||||||
|
|
||||||
|
@wsme_pecan.wsexpose([live_host.LiveHost], body=live_query.LiveQuery)
|
||||||
|
def post(self, query):
|
||||||
|
"""Given a LiveQuery, returns all matching hosts."""
|
||||||
|
handler = live_host_handler.HostHandler(pecan.request)
|
||||||
|
hosts = handler.get_all(live_query=query)
|
||||||
|
return hosts
|
||||||
|
|
||||||
@pecan.expose()
|
@pecan.expose()
|
||||||
def _lookup(self, host_name, *remainder):
|
def _lookup(self, host_name, *remainder):
|
||||||
@ -37,7 +49,7 @@ class HostController(rest.RestController):
|
|||||||
# services = ServicesController()
|
# services = ServicesController()
|
||||||
# See init for controller creation. We need host_name to instanciate it
|
# See init for controller creation. We need host_name to instanciate it
|
||||||
# externalcommands = ExternalCommandsController()
|
# externalcommands = ExternalCommandsController()
|
||||||
config = config.ConfigController()
|
# config = config.ConfigController()
|
||||||
events = logs.LogsController()
|
events = logs.LogsController()
|
||||||
metrics = metrics.MetricsController()
|
metrics = metrics.MetricsController()
|
||||||
|
|
||||||
@ -52,3 +64,11 @@ class HostController(rest.RestController):
|
|||||||
output = '{"host_name": "myhostname", "alias": %s}' % self._id
|
output = '{"host_name": "myhostname", "alias": %s}' % self._id
|
||||||
|
|
||||||
return output
|
return output
|
||||||
|
|
||||||
|
|
||||||
|
class ConfigController(rest.RestController):
|
||||||
|
|
||||||
|
@pecan.expose()
|
||||||
|
def get_all(self):
|
||||||
|
"""Returns config from a specific host."""
|
||||||
|
return "Dump CONFIG"
|
@ -1,24 +0,0 @@
|
|||||||
# Copyright 2014 - Savoir-Faire Linux inc.
|
|
||||||
#
|
|
||||||
# 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.
|
|
||||||
|
|
||||||
import pecan
|
|
||||||
from pecan import rest
|
|
||||||
|
|
||||||
|
|
||||||
class ConfigController(rest.RestController):
|
|
||||||
|
|
||||||
@pecan.expose()
|
|
||||||
def get_all(self):
|
|
||||||
"""Returns config from a specific host."""
|
|
||||||
return "Dump CONFIG"
|
|
38
surveil/api/controllers/v2/status/services.py
Normal file
38
surveil/api/controllers/v2/status/services.py
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
# Copyright 2014 - Savoir-Faire Linux inc.
|
||||||
|
#
|
||||||
|
# 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.
|
||||||
|
|
||||||
|
import pecan
|
||||||
|
from pecan import rest
|
||||||
|
import wsmeext.pecan as wsme_pecan
|
||||||
|
|
||||||
|
from surveil.api.datamodel.status import live_query
|
||||||
|
from surveil.api.datamodel.status import live_service
|
||||||
|
from surveil.api.handlers.status import live_service_handler
|
||||||
|
|
||||||
|
|
||||||
|
class ServicesController(rest.RestController):
|
||||||
|
|
||||||
|
@wsme_pecan.wsexpose([live_service.LiveService])
|
||||||
|
def get_all(self):
|
||||||
|
"""Returns all services."""
|
||||||
|
handler = live_service_handler.ServiceHandler(pecan.request)
|
||||||
|
services = handler.get_all()
|
||||||
|
return services
|
||||||
|
|
||||||
|
@wsme_pecan.wsexpose([live_service.LiveService], body=live_query.LiveQuery)
|
||||||
|
def post(self, query):
|
||||||
|
"""Given a LiveQuery, returns all matching services."""
|
||||||
|
handler = live_service_handler.ServiceHandler(pecan.request)
|
||||||
|
services = handler.get_all(live_query=query)
|
||||||
|
return services
|
0
surveil/api/datamodel/status/__init__.py
Normal file
0
surveil/api/datamodel/status/__init__.py
Normal file
49
surveil/api/datamodel/status/live_host.py
Normal file
49
surveil/api/datamodel/status/live_host.py
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
# Copyright 2014 - Savoir-Faire Linux inc.
|
||||||
|
#
|
||||||
|
# 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.
|
||||||
|
|
||||||
|
import wsme
|
||||||
|
import wsme.types as wtypes
|
||||||
|
|
||||||
|
from surveil.api.controllers.v1.datamodel import types
|
||||||
|
|
||||||
|
|
||||||
|
class LiveHost(types.Base):
|
||||||
|
host_name = wsme.wsattr(wtypes.text, mandatory=False)
|
||||||
|
"""The name of the host"""
|
||||||
|
|
||||||
|
description = wsme.wsattr(wtypes.text, mandatory=False)
|
||||||
|
"""The description of the host"""
|
||||||
|
|
||||||
|
state = wsme.wsattr(int, mandatory=False)
|
||||||
|
"""The current state of the host"""
|
||||||
|
|
||||||
|
last_check = wsme.wsattr(int, mandatory=False)
|
||||||
|
"""The last time the host was checked"""
|
||||||
|
|
||||||
|
last_state_change = wsme.wsattr(int, mandatory=False)
|
||||||
|
"""The last time the state has changed"""
|
||||||
|
|
||||||
|
plugin_output = wsme.wsattr(wtypes.text, mandatory=False)
|
||||||
|
"""Plugin output of the last check"""
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def sample(cls):
|
||||||
|
return cls(
|
||||||
|
host_name='CoolHost',
|
||||||
|
description='Very Nice Host',
|
||||||
|
state=0,
|
||||||
|
last_check=1429220785,
|
||||||
|
last_state_change=1429220785,
|
||||||
|
plugin_output='PING OK - Packet loss = 0%, RTA = 0.02 ms'
|
||||||
|
)
|
42
surveil/api/datamodel/status/live_query.py
Normal file
42
surveil/api/datamodel/status/live_query.py
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
# Copyright 2014 - Savoir-Faire Linux inc.
|
||||||
|
#
|
||||||
|
# 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.
|
||||||
|
|
||||||
|
import json
|
||||||
|
|
||||||
|
import wsme
|
||||||
|
import wsme.types as wtypes
|
||||||
|
|
||||||
|
from surveil.api.controllers.v1.datamodel import types
|
||||||
|
|
||||||
|
|
||||||
|
class LiveQuery(types.Base):
|
||||||
|
"""Holds a sample query encoded in json."""
|
||||||
|
|
||||||
|
filters = wsme.wsattr(wtypes.text, mandatory=True)
|
||||||
|
"The filter expression encoded in json."
|
||||||
|
|
||||||
|
fields = wsme.wsattr(wtypes.text, mandatory=True)
|
||||||
|
"List of fields to include in the response."
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def sample(cls):
|
||||||
|
return cls(
|
||||||
|
fields=json.dumps(['host_name', 'last_check']),
|
||||||
|
filters=json.dumps({
|
||||||
|
"isnot": {
|
||||||
|
"state": ["0", "1"],
|
||||||
|
"host_state": ["2"]
|
||||||
|
}
|
||||||
|
})
|
||||||
|
)
|
53
surveil/api/datamodel/status/live_service.py
Normal file
53
surveil/api/datamodel/status/live_service.py
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
# Copyright 2014 - Savoir-Faire Linux inc.
|
||||||
|
#
|
||||||
|
# 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.
|
||||||
|
|
||||||
|
import wsme
|
||||||
|
import wsme.types as wtypes
|
||||||
|
|
||||||
|
from surveil.api.controllers.v1.datamodel import types
|
||||||
|
|
||||||
|
|
||||||
|
class LiveService(types.Base):
|
||||||
|
host_name = wsme.wsattr(wtypes.text, mandatory=False)
|
||||||
|
"""The host for the service"""
|
||||||
|
|
||||||
|
service_description = wsme.wsattr(wtypes.text, mandatory=False)
|
||||||
|
"""The name of the service"""
|
||||||
|
|
||||||
|
description = wsme.wsattr(wtypes.text, mandatory=False)
|
||||||
|
"""The description of the sevice"""
|
||||||
|
|
||||||
|
state = wsme.wsattr(int, mandatory=False)
|
||||||
|
"""The current state of the service"""
|
||||||
|
|
||||||
|
last_check = wsme.wsattr(int, mandatory=False)
|
||||||
|
"""The last time the service was checked"""
|
||||||
|
|
||||||
|
last_state_change = wsme.wsattr(int, mandatory=False)
|
||||||
|
"""The last time the state has changed"""
|
||||||
|
|
||||||
|
plugin_output = wsme.wsattr(wtypes.text, mandatory=False)
|
||||||
|
"""Plugin output of the last check"""
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def sample(cls):
|
||||||
|
return cls(
|
||||||
|
host_name='Webserver',
|
||||||
|
service_name='Apache',
|
||||||
|
description='Serves Stuff',
|
||||||
|
state=0,
|
||||||
|
last_check=1429220785,
|
||||||
|
last_state_change=1429220785,
|
||||||
|
plugin_output='HTTP OK - GOT NICE RESPONSE'
|
||||||
|
)
|
0
surveil/api/handlers/status/__init__.py
Normal file
0
surveil/api/handlers/status/__init__.py
Normal file
52
surveil/api/handlers/status/liveQuery_filter.py
Normal file
52
surveil/api/handlers/status/liveQuery_filter.py
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
# Copyright 2014 - Savoir-Faire Linux inc.
|
||||||
|
#
|
||||||
|
# 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.
|
||||||
|
|
||||||
|
import json
|
||||||
|
|
||||||
|
|
||||||
|
def filter_dict_list_with_live_query(item_list, live_query):
|
||||||
|
filters = json.loads(live_query.filters)
|
||||||
|
|
||||||
|
matching_items = []
|
||||||
|
|
||||||
|
for item in item_list:
|
||||||
|
matches = True
|
||||||
|
|
||||||
|
# Filters are, for example, 'isnot' or 'is'
|
||||||
|
for filter in filters.items():
|
||||||
|
|
||||||
|
# Fields are, for example, 'STATE'
|
||||||
|
for field in filter[1].items():
|
||||||
|
|
||||||
|
# Values are, for example, 0, 1, UP, Down...
|
||||||
|
for value in field[1]:
|
||||||
|
|
||||||
|
if filter[0] == "isnot":
|
||||||
|
if item[field[0]] == value:
|
||||||
|
matches = False
|
||||||
|
break
|
||||||
|
elif filter[0] == "is":
|
||||||
|
if item[field[0]] != value:
|
||||||
|
matches = False
|
||||||
|
break
|
||||||
|
|
||||||
|
if matches:
|
||||||
|
fields = json.loads(live_query.fields)
|
||||||
|
matching_item = {}
|
||||||
|
for field in fields:
|
||||||
|
matching_item[field] = item[field]
|
||||||
|
|
||||||
|
matching_items.append(matching_item)
|
||||||
|
|
||||||
|
return matching_items
|
58
surveil/api/handlers/status/live_host_handler.py
Normal file
58
surveil/api/handlers/status/live_host_handler.py
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
# Copyright 2014 - Savoir-Faire Linux inc.
|
||||||
|
#
|
||||||
|
# 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.
|
||||||
|
|
||||||
|
from __future__ import print_function
|
||||||
|
|
||||||
|
from surveil.api.datamodel.status import live_host
|
||||||
|
from surveil.api.handlers import handler
|
||||||
|
from surveil.api.handlers.status import liveQuery_filter as query_filter
|
||||||
|
|
||||||
|
|
||||||
|
class HostHandler(handler.Handler):
|
||||||
|
"""Fulfills a request on the live hosts."""
|
||||||
|
|
||||||
|
def get_all(self, live_query=None):
|
||||||
|
"""Return all live hosts."""
|
||||||
|
cli = self.request.influxdb_client
|
||||||
|
query = "SELECT * from HOST_STATE GROUP BY host_name LIMIT 1"
|
||||||
|
response = cli.query(query)
|
||||||
|
|
||||||
|
host_dicts = []
|
||||||
|
|
||||||
|
for item in response.items():
|
||||||
|
first_entry = next(item[1])
|
||||||
|
|
||||||
|
host_dict = {
|
||||||
|
"host_name": item[0][1]['host_name'],
|
||||||
|
"description": item[0][1]['host_name'],
|
||||||
|
"state": first_entry['state'],
|
||||||
|
"last_check": int(first_entry['last_check']),
|
||||||
|
"last_state_change": int(first_entry['last_state_change']),
|
||||||
|
"plugin_output": first_entry['output']
|
||||||
|
}
|
||||||
|
|
||||||
|
host_dicts.append(host_dict)
|
||||||
|
|
||||||
|
if live_query:
|
||||||
|
host_dicts = query_filter.filter_dict_list_with_live_query(
|
||||||
|
host_dicts,
|
||||||
|
live_query
|
||||||
|
)
|
||||||
|
|
||||||
|
hosts = []
|
||||||
|
for host_dict in host_dicts:
|
||||||
|
host = live_host.LiveHost(**host_dict)
|
||||||
|
hosts.append(host)
|
||||||
|
|
||||||
|
return hosts
|
64
surveil/api/handlers/status/live_service_handler.py
Normal file
64
surveil/api/handlers/status/live_service_handler.py
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
# Copyright 2014 - Savoir-Faire Linux inc.
|
||||||
|
#
|
||||||
|
# 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.
|
||||||
|
|
||||||
|
from __future__ import print_function
|
||||||
|
|
||||||
|
from surveil.api.datamodel.status import live_service
|
||||||
|
from surveil.api.handlers import handler
|
||||||
|
from surveil.api.handlers.status import liveQuery_filter as query_filter
|
||||||
|
|
||||||
|
|
||||||
|
class ServiceHandler(handler.Handler):
|
||||||
|
"""Fulfills a request on live services."""
|
||||||
|
|
||||||
|
def get_all(self, live_query=None):
|
||||||
|
"""Return all live services."""
|
||||||
|
cli = self.request.influxdb_client
|
||||||
|
query = (
|
||||||
|
"SELECT * from SERVICE_STATE "
|
||||||
|
"GROUP BY host_name, service_description "
|
||||||
|
"LIMIT 1"
|
||||||
|
)
|
||||||
|
|
||||||
|
response = cli.query(query)
|
||||||
|
|
||||||
|
service_dicts = []
|
||||||
|
|
||||||
|
for item in response.items():
|
||||||
|
first_entry = next(item[1])
|
||||||
|
|
||||||
|
service_dict = {
|
||||||
|
"service_description": item[0][1]['service_description'],
|
||||||
|
"host_name": item[0][1]['host_name'],
|
||||||
|
"description": item[0][1]['service_description'],
|
||||||
|
"state": first_entry['state'],
|
||||||
|
"last_check": int(first_entry['last_check']),
|
||||||
|
"last_state_change": int(first_entry['last_state_change']),
|
||||||
|
"plugin_output": first_entry['output']
|
||||||
|
}
|
||||||
|
|
||||||
|
service_dicts.append(service_dict)
|
||||||
|
|
||||||
|
if live_query:
|
||||||
|
service_dicts = query_filter.filter_dict_list_with_live_query(
|
||||||
|
service_dicts,
|
||||||
|
live_query
|
||||||
|
)
|
||||||
|
|
||||||
|
services = []
|
||||||
|
for service_dict in service_dicts:
|
||||||
|
service = live_service.LiveService(**service_dict)
|
||||||
|
services.append(service)
|
||||||
|
|
||||||
|
return services
|
@ -12,21 +12,26 @@
|
|||||||
# 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 influxdb
|
||||||
from pecan import hooks
|
from pecan import hooks
|
||||||
import pymongo
|
import pymongo
|
||||||
|
|
||||||
|
|
||||||
class DBHook(hooks.PecanHook):
|
class DBHook(hooks.PecanHook):
|
||||||
|
|
||||||
def __init__(self, mongo_url, ws_arbiter_url):
|
def __init__(self, mongo_url, ws_arbiter_url, influxdb_url):
|
||||||
self.mongo_url = mongo_url
|
self.mongo_url = mongo_url
|
||||||
self.ws_arbiter_url = ws_arbiter_url
|
self.ws_arbiter_url = ws_arbiter_url
|
||||||
|
self.influxdb_url = influxdb_url
|
||||||
|
|
||||||
def before(self, state):
|
def before(self, state):
|
||||||
self.mongoclient = pymongo.MongoClient(self.mongo_url)
|
self.mongoclient = pymongo.MongoClient(self.mongo_url)
|
||||||
|
|
||||||
state.request.mongo_connection = self.mongoclient
|
state.request.mongo_connection = self.mongoclient
|
||||||
state.request.ws_arbiter_url = self.ws_arbiter_url
|
state.request.ws_arbiter_url = self.ws_arbiter_url
|
||||||
|
state.request.influxdb_client = influxdb.InfluxDBClient.from_DSN(
|
||||||
|
self.influxdb_url
|
||||||
|
)
|
||||||
|
|
||||||
def after(self, state):
|
def after(self, state):
|
||||||
self.mongoclient.close()
|
self.mongoclient.close()
|
||||||
|
0
surveil/tests/api/controllers/v2/status/__init__.py
Normal file
0
surveil/tests/api/controllers/v2/status/__init__.py
Normal file
93
surveil/tests/api/controllers/v2/status/test_hosts.py
Normal file
93
surveil/tests/api/controllers/v2/status/test_hosts.py
Normal file
@ -0,0 +1,93 @@
|
|||||||
|
# Copyright 2015 - Savoir-Faire Linux inc.
|
||||||
|
#
|
||||||
|
# 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.
|
||||||
|
|
||||||
|
import json
|
||||||
|
|
||||||
|
import httpretty
|
||||||
|
|
||||||
|
from surveil.tests.api import functionalTest
|
||||||
|
|
||||||
|
|
||||||
|
class TestStatusHosts(functionalTest.FunctionalTest):
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
super(TestStatusHosts, self).setUp()
|
||||||
|
self.influxdb_response = (
|
||||||
|
'{"results":[{"series":[{"name":"HOST_STATE","tags":{"host_nam'
|
||||||
|
'e":"localhost"},"columns":["time","last_check","last_state_chan'
|
||||||
|
'ge","output","state","state_type"],"values":[["201'
|
||||||
|
'5-04-19T01:09:24Z",1.429405764e+09,1.429405765316929e+09,"OK '
|
||||||
|
'- localhost: rta 0.033ms, lost 0%",0,"HARD"]]},{"name":"'
|
||||||
|
'HOST_STATE","tags":{"host_name":"test_keystone"},"columns":["'
|
||||||
|
'time","last_check","last_state_change","output","state",'
|
||||||
|
'"state_type"],"values":[["2015-04-19T01:09:23Z",1.4294057'
|
||||||
|
'63e+09,1.429405765317144e+09,"OK - 127.0.0.1: rta 0.032ms, lo'
|
||||||
|
'st 0%",0,"HARD"]]},{"name":"HOST_STATE","tags":{"host_na'
|
||||||
|
'me":"ws-arbiter"},"columns":["time","last_check","last_state_ch'
|
||||||
|
'ange","output","state","state_type"],"values":[["2'
|
||||||
|
'015-04-19T01:09:24Z",1.429405764e+09,1.429405765317063e+09,"O'
|
||||||
|
'K - localhost: rta 0.030ms, lost 0%",0,"HARD"]]}]}]}'
|
||||||
|
)
|
||||||
|
|
||||||
|
@httpretty.activate
|
||||||
|
def test_get_all_hosts(self):
|
||||||
|
httpretty.register_uri(httpretty.GET,
|
||||||
|
"http://influxdb:8086/query",
|
||||||
|
body=self.influxdb_response)
|
||||||
|
|
||||||
|
response = self.app.get("/v2/status/hosts")
|
||||||
|
|
||||||
|
expected = [
|
||||||
|
{"description": "localhost",
|
||||||
|
"last_state_change": 1429405765,
|
||||||
|
"plugin_output": "OK - localhost: rta 0.033ms, lost 0%",
|
||||||
|
"last_check": 1429405764,
|
||||||
|
"state": 0,
|
||||||
|
"host_name": "localhost"},
|
||||||
|
{"description": "test_keystone",
|
||||||
|
"last_state_change": 1429405765,
|
||||||
|
"plugin_output": "OK - 127.0.0.1: rta 0.032ms, lost 0%",
|
||||||
|
"last_check": 1429405763,
|
||||||
|
"state": 0,
|
||||||
|
"host_name": "test_keystone"},
|
||||||
|
{"description": "ws-arbiter",
|
||||||
|
"last_state_change": 1429405765,
|
||||||
|
"plugin_output": "OK - localhost: rta 0.030ms, lost 0%",
|
||||||
|
"last_check": 1429405764,
|
||||||
|
"state": 0,
|
||||||
|
"host_name": "ws-arbiter"}]
|
||||||
|
|
||||||
|
self.assertEqual(json.loads(response.body), expected)
|
||||||
|
|
||||||
|
@httpretty.activate
|
||||||
|
def test_query_hosts(self):
|
||||||
|
httpretty.register_uri(httpretty.GET,
|
||||||
|
"http://influxdb:8086/query",
|
||||||
|
body=self.influxdb_response)
|
||||||
|
|
||||||
|
query = {
|
||||||
|
'fields': json.dumps(['host_name', 'last_check']),
|
||||||
|
'filters': json.dumps({
|
||||||
|
"isnot": {
|
||||||
|
"host_name": ['localhost'],
|
||||||
|
"description": ["test_keystone"]
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
response = self.app.post_json("/v2/status/hosts", params=query)
|
||||||
|
|
||||||
|
expected = [{"host_name": "ws-arbiter", "last_check": 1429405764}]
|
||||||
|
|
||||||
|
self.assertEqual(json.loads(response.body), expected)
|
94
surveil/tests/api/controllers/v2/status/test_services.py
Normal file
94
surveil/tests/api/controllers/v2/status/test_services.py
Normal file
@ -0,0 +1,94 @@
|
|||||||
|
# Copyright 2015 - Savoir-Faire Linux inc.
|
||||||
|
#
|
||||||
|
# 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.
|
||||||
|
|
||||||
|
import json
|
||||||
|
|
||||||
|
import httpretty
|
||||||
|
|
||||||
|
from surveil.tests.api import functionalTest
|
||||||
|
|
||||||
|
|
||||||
|
class TestStatusServices(functionalTest.FunctionalTest):
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
super(TestStatusServices, self).setUp()
|
||||||
|
self.influxdb_response = (
|
||||||
|
'{"results":[{"series":[{"name":"SERVICE_STATE","tags":{"host_nam'
|
||||||
|
'e":"test_keystone","service_description":"Check KeyStone service'
|
||||||
|
'."},"columns":["time","last_check","last_state_change","output",'
|
||||||
|
'"state","state_type"],"values":[["2015-04-19T18:20:34Z",1.429467'
|
||||||
|
'634e+09,1.429467636632134e+09,"There was no suitable authenticat'
|
||||||
|
'ion url for this request",3,"SOFT"]]},{"name":"SERVICE_STATE","t'
|
||||||
|
'ags":{"host_name":"ws-arbiter","service_description":"check-ws-a'
|
||||||
|
'rbiter"},"columns":["time","last_check","last_state_change","out'
|
||||||
|
'put","state","state_type"],"values":[["2015-04-19T18:20:33Z",1.4'
|
||||||
|
'29467633e+09,1.429467635629833e+09,"TCP OK - 0.000 second respon'
|
||||||
|
'se time on port 7760",0,"HARD"]]}]}]}'
|
||||||
|
)
|
||||||
|
|
||||||
|
@httpretty.activate
|
||||||
|
def test_get_all_services(self):
|
||||||
|
httpretty.register_uri(httpretty.GET,
|
||||||
|
"http://influxdb:8086/query",
|
||||||
|
body=self.influxdb_response)
|
||||||
|
|
||||||
|
response = self.app.get("/v2/status/services")
|
||||||
|
|
||||||
|
expected = [
|
||||||
|
{'description': 'Check KeyStone service.',
|
||||||
|
'last_state_change': 1429467636,
|
||||||
|
'plugin_output':
|
||||||
|
'There was no suitable authentication url for this request',
|
||||||
|
'last_check': 1429467634,
|
||||||
|
'state': 3,
|
||||||
|
'host_name': 'test_keystone',
|
||||||
|
'service_description': 'Check KeyStone service.'},
|
||||||
|
{'description': 'check-ws-arbiter',
|
||||||
|
'last_state_change': 1429467635,
|
||||||
|
'plugin_output':
|
||||||
|
'TCP OK - 0.000 second response time on port 7760',
|
||||||
|
'last_check': 1429467633,
|
||||||
|
'state': 0,
|
||||||
|
'host_name': 'ws-arbiter',
|
||||||
|
'service_description': 'check-ws-arbiter'}
|
||||||
|
]
|
||||||
|
|
||||||
|
self.assertEqual(json.loads(response.body), expected)
|
||||||
|
|
||||||
|
@httpretty.activate
|
||||||
|
def test_query_services(self):
|
||||||
|
httpretty.register_uri(httpretty.GET,
|
||||||
|
"http://influxdb:8086/query",
|
||||||
|
body=self.influxdb_response)
|
||||||
|
|
||||||
|
query = {
|
||||||
|
'fields': json.dumps(['host_name', 'service_description']),
|
||||||
|
'filters': json.dumps({
|
||||||
|
"isnot": {
|
||||||
|
"host_name": ['ws-arbiter'],
|
||||||
|
},
|
||||||
|
"is": {
|
||||||
|
"service_description": ["Check KeyStone service."]
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
response = self.app.post_json("/v2/status/services", params=query)
|
||||||
|
|
||||||
|
expected = [
|
||||||
|
{'host_name': 'test_keystone',
|
||||||
|
'service_description': 'Check KeyStone service.'}
|
||||||
|
]
|
||||||
|
|
||||||
|
self.assertEqual(json.loads(response.body), expected)
|
@ -14,6 +14,7 @@
|
|||||||
|
|
||||||
import os
|
import os
|
||||||
|
|
||||||
|
import influxdb
|
||||||
import mongomock
|
import mongomock
|
||||||
from oslo_config import cfg
|
from oslo_config import cfg
|
||||||
import pecan
|
import pecan
|
||||||
@ -37,20 +38,26 @@ class FunctionalTest(base.BaseTestCase):
|
|||||||
|
|
||||||
self.mongoconnection = mongomock.Connection()
|
self.mongoconnection = mongomock.Connection()
|
||||||
self.ws_arbiter_url = "http://localhost:7760"
|
self.ws_arbiter_url = "http://localhost:7760"
|
||||||
|
self.influxdb_client = influxdb.InfluxDBClient.from_DSN(
|
||||||
|
'influxdb://root:root@influxdb:8086/db'
|
||||||
|
)
|
||||||
|
|
||||||
class TestHook(hooks.PecanHook):
|
class TestHook(hooks.PecanHook):
|
||||||
def __init__(self, mongoclient, wsarbiterurl):
|
def __init__(self, mongoclient, wsarbiterurl, influxdb_client):
|
||||||
self.mongoclient = mongoclient
|
self.mongoclient = mongoclient
|
||||||
self.ws_arbiter_url = wsarbiterurl
|
self.ws_arbiter_url = wsarbiterurl
|
||||||
|
self.influxdb_client = influxdb_client
|
||||||
|
|
||||||
def before(self, state):
|
def before(self, state):
|
||||||
state.request.mongo_connection = self.mongoclient
|
state.request.mongo_connection = self.mongoclient
|
||||||
state.request.ws_arbiter_url = self.ws_arbiter_url
|
state.request.ws_arbiter_url = self.ws_arbiter_url
|
||||||
|
state.request.influxdb_client = self.influxdb_client
|
||||||
|
|
||||||
app_hooks = [
|
app_hooks = [
|
||||||
TestHook(
|
TestHook(
|
||||||
self.mongoconnection,
|
self.mongoconnection,
|
||||||
self.ws_arbiter_url
|
self.ws_arbiter_url,
|
||||||
|
self.influxdb_client
|
||||||
)
|
)
|
||||||
]
|
]
|
||||||
|
|
||||||
|
0
surveil/tests/api/handlers/__init__.py
Normal file
0
surveil/tests/api/handlers/__init__.py
Normal file
0
surveil/tests/api/handlers/live/__init__.py
Normal file
0
surveil/tests/api/handlers/live/__init__.py
Normal file
86
surveil/tests/api/handlers/live/test_liveQuery_filter.py
Normal file
86
surveil/tests/api/handlers/live/test_liveQuery_filter.py
Normal file
@ -0,0 +1,86 @@
|
|||||||
|
# Copyright 2015 - Savoir-Faire Linux inc.
|
||||||
|
#
|
||||||
|
# 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.
|
||||||
|
|
||||||
|
import json
|
||||||
|
|
||||||
|
from surveil.api.datamodel.status import live_query
|
||||||
|
from surveil.api.handlers.status import liveQuery_filter as query_filter
|
||||||
|
from surveil.tests import base
|
||||||
|
|
||||||
|
|
||||||
|
class LiveQueryFilterTest(base.BaseTestCase):
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
self.items = [
|
||||||
|
{"description": "localhost",
|
||||||
|
"last_state_change": 1429400991,
|
||||||
|
"plugin_output": "OK - localhost: rta 0.047ms, lost 0%",
|
||||||
|
"last_check": 1429400990,
|
||||||
|
"state": 0,
|
||||||
|
"host_name": "localhost"},
|
||||||
|
{"description": "test_keystone",
|
||||||
|
"last_state_change": 1429400986,
|
||||||
|
"plugin_output": "OK - 127.0.0.1: rta 0.045ms, lost 0%",
|
||||||
|
"last_check": 1429400984, "state": 2,
|
||||||
|
"host_name": "test_keystone"},
|
||||||
|
{"description": "ws-arbiter",
|
||||||
|
"last_state_change": 1429400991,
|
||||||
|
"plugin_output": "OK - localhost: rta 0.042ms, lost 0%",
|
||||||
|
"last_check": 1429400990,
|
||||||
|
"state": 2,
|
||||||
|
"host_name": "ws-arbiter"}
|
||||||
|
]
|
||||||
|
|
||||||
|
def test_query_builder_filter_isnot(self):
|
||||||
|
query = live_query.LiveQuery(
|
||||||
|
fields=json.dumps(['host_name', 'last_check']),
|
||||||
|
filters=json.dumps({
|
||||||
|
"isnot": {
|
||||||
|
"state": [0, 1],
|
||||||
|
"description": ["test_keystone"]
|
||||||
|
}
|
||||||
|
})
|
||||||
|
)
|
||||||
|
|
||||||
|
result = query_filter.filter_dict_list_with_live_query(
|
||||||
|
self.items,
|
||||||
|
query
|
||||||
|
)
|
||||||
|
|
||||||
|
expected = [{"last_check": 1429400990, "host_name": "ws-arbiter"}]
|
||||||
|
|
||||||
|
self.assertItemsEqual(result, expected)
|
||||||
|
|
||||||
|
def test_query_builder_filter_is(self):
|
||||||
|
query = live_query.LiveQuery(
|
||||||
|
fields=json.dumps(['host_name']),
|
||||||
|
filters=json.dumps({
|
||||||
|
"is": {
|
||||||
|
"state": [0],
|
||||||
|
"description": ["localhost"]
|
||||||
|
},
|
||||||
|
"isnot": {
|
||||||
|
"state": [1]
|
||||||
|
}
|
||||||
|
})
|
||||||
|
)
|
||||||
|
|
||||||
|
result = query_filter.filter_dict_list_with_live_query(
|
||||||
|
self.items,
|
||||||
|
query
|
||||||
|
)
|
||||||
|
|
||||||
|
expected = [{"host_name": "localhost"}]
|
||||||
|
|
||||||
|
self.assertItemsEqual(result, expected)
|
Loading…
Reference in New Issue
Block a user