From f4be811794c6abebf21732a21038fee7116f77e6 Mon Sep 17 00:00:00 2001 From: Vincent Fournier Date: Wed, 1 Jul 2015 22:10:40 -0400 Subject: [PATCH] Refactored status controllers and handlers Change-Id: I105c2da923e7e03e14d5dd9f003cc1c9bbf5de39 --- doc/source/webapi/v2/status.rst | 31 ++- surveil/api/controllers/v2/status/__init__.py | 25 -- .../controllers/v2/status/events/__init__.py | 0 .../v2/status/{ => events}/events.py | 1 - surveil/api/controllers/v2/status/hosts.py | 256 ------------------ .../controllers/v2/status/hosts/__init__.py | 0 .../api/controllers/v2/status/hosts/hosts.py | 80 ++++++ .../controllers/v2/status/hosts/metrics.py | 70 +++++ .../controllers/v2/status/hosts/results.py | 39 +++ .../v2/status/hosts/services/__init__.py | 0 .../v2/status/hosts/services/metrics.py | 75 +++++ .../v2/status/hosts/services/results.py | 43 +++ .../v2/status/hosts/services/services.py | 53 ++++ .../v2/status/services/__init__.py | 0 .../v2/status/{ => services}/services.py | 0 surveil/api/controllers/v2/status/status.py | 25 ++ surveil/api/controllers/v2/v2.py | 2 +- .../status/metrics/live_metric_handler.py | 41 +-- .../status/metrics/metric_name_handler.py | 56 ++++ .../v2/status/test_hosts_metric.py | 108 -------- .../v2/status/test_hosts_services_metrics.py | 233 ++++++++++++++++ 21 files changed, 703 insertions(+), 435 deletions(-) create mode 100644 surveil/api/controllers/v2/status/events/__init__.py rename surveil/api/controllers/v2/status/{ => events}/events.py (99%) delete mode 100644 surveil/api/controllers/v2/status/hosts.py create mode 100644 surveil/api/controllers/v2/status/hosts/__init__.py create mode 100644 surveil/api/controllers/v2/status/hosts/hosts.py create mode 100644 surveil/api/controllers/v2/status/hosts/metrics.py create mode 100644 surveil/api/controllers/v2/status/hosts/results.py create mode 100644 surveil/api/controllers/v2/status/hosts/services/__init__.py create mode 100644 surveil/api/controllers/v2/status/hosts/services/metrics.py create mode 100644 surveil/api/controllers/v2/status/hosts/services/results.py create mode 100644 surveil/api/controllers/v2/status/hosts/services/services.py create mode 100644 surveil/api/controllers/v2/status/services/__init__.py rename surveil/api/controllers/v2/status/{ => services}/services.py (100%) create mode 100644 surveil/api/controllers/v2/status/status.py create mode 100644 surveil/api/handlers/status/metrics/metric_name_handler.py create mode 100644 surveil/tests/api/controllers/v2/status/test_hosts_services_metrics.py diff --git a/doc/source/webapi/v2/status.rst b/doc/source/webapi/v2/status.rst index a5b5a8c..db6ba79 100644 --- a/doc/source/webapi/v2/status.rst +++ b/doc/source/webapi/v2/status.rst @@ -4,46 +4,49 @@ Status ====== -.. rest-controller:: surveil.api.controllers.v2.status:StatusController +.. rest-controller:: surveil.api.controllers.v2.status.status:StatusController :webprefix: /v2/status +Events +====== + +.. rest-controller:: surveil.api.controllers.v2.status.events.events:EventsController +:webprefix: /v2/status/events/ + + Hosts ===== -.. rest-controller:: surveil.api.controllers.v2.status.hosts:HostsController +.. rest-controller:: surveil.api.controllers.v2.status.hosts.hosts:HostsController :webprefix: /v2/status/hosts -.. rest-controller:: surveil.api.controllers.v2.status.hosts:HostController +.. rest-controller:: surveil.api.controllers.v2.status.hosts.hosts:HostController :webprefix: /v2/status/hosts/ -.. rest-controller:: surveil.api.controllers.v2.status.hosts:ConfigController +.. rest-controller:: surveil.api.controllers.v2.status.hosts.hosts:ConfigController :webprefix: /v2/status/hosts/(host_name)/config -.. rest-controller:: surveil.api.controllers.v2.status.hosts:HostCheckResultsSubController +.. rest-controller:: surveil.api.controllers.v2.status.hosts.results:CheckResultsSubController :webprefix: /v2/status/hosts/(host_name)/results -.. rest-controller:: surveil.api.controllers.v2.status.hosts:HostMetricsController +.. rest-controller:: surveil.api.controllers.v2.status.hosts.metrics:MetricsController :webprefix: /v2/status/hosts/(host_name)/metrics -.. rest-controller:: surveil.api.controllers.v2.status.hosts:HostMetricController +.. rest-controller:: surveil.api.controllers.v2.status.hosts.metrics:MetricController :webprefix: /v2/status/hosts/(host_name)/metrics -.. rest-controller:: surveil.api.controllers.v2.status.hosts:ServiceCheckResultsSubController +.. rest-controller:: surveil.api.controllers.v2.status.hosts.services.results:CheckResultsSubController :webprefix: /v2/status/hosts/(host_name)/services/(service_description)/results -.. rest-controller:: surveil.api.controllers.v2.status.hosts:HostServiceMetricsController +.. rest-controller:: surveil.api.controllers.v2.status.hosts.services.metrics:MetricsController :webprefix: /v2/status/hosts/(host_name)/services/(service_description)/metrics -.. rest-controller:: surveil.api.controllers.v2.status.events:EventsController - :webprefix: /v2/status/events/ - - Services ======== -.. rest-controller:: surveil.api.controllers.v2.status.services:ServicesController +.. rest-controller:: surveil.api.controllers.v2.status.services.services:ServicesController :webprefix: /v2/status/services diff --git a/surveil/api/controllers/v2/status/__init__.py b/surveil/api/controllers/v2/status/__init__.py index 372eea6..e69de29 100644 --- a/surveil/api/controllers/v2/status/__init__.py +++ b/surveil/api/controllers/v2/status/__init__.py @@ -1,25 +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. - -from pecan import rest - -from surveil.api.controllers.v2.status import events as v2_events -from surveil.api.controllers.v2.status import hosts as v2_hosts -from surveil.api.controllers.v2.status import services as v2_services - - -class StatusController(rest.RestController): - hosts = v2_hosts.HostsController() - services = v2_services.ServicesController() - events = v2_events.EventsController() diff --git a/surveil/api/controllers/v2/status/events/__init__.py b/surveil/api/controllers/v2/status/events/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/surveil/api/controllers/v2/status/events.py b/surveil/api/controllers/v2/status/events/events.py similarity index 99% rename from surveil/api/controllers/v2/status/events.py rename to surveil/api/controllers/v2/status/events/events.py index 38fdd63..f73ddaf 100644 --- a/surveil/api/controllers/v2/status/events.py +++ b/surveil/api/controllers/v2/status/events/events.py @@ -16,7 +16,6 @@ import pecan from pecan import rest import wsmeext.pecan as wsme_pecan - from surveil.api.datamodel.status import event from surveil.api.datamodel.status import live_query from surveil.api.handlers.status import event_handler diff --git a/surveil/api/controllers/v2/status/hosts.py b/surveil/api/controllers/v2/status/hosts.py deleted file mode 100644 index 15ffc0c..0000000 --- a/surveil/api/controllers/v2/status/hosts.py +++ /dev/null @@ -1,256 +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 -import requests -import wsmeext.pecan as wsme_pecan - -from surveil.api.controllers.v2.status import events -from surveil.api.datamodel import checkresult -from surveil.api.datamodel.status import live_host -from surveil.api.datamodel.status import live_query -from surveil.api.datamodel.status import live_service -from surveil.api.datamodel.status.metrics import live_metric -from surveil.api.handlers.status import live_host_handler -from surveil.api.handlers.status import live_service_handler -from surveil.api.handlers.status.metrics import live_metric_handler -from surveil.common import util - - -class HostsController(rest.RestController): - - @util.policy_enforce(['authenticated']) - @wsme_pecan.wsexpose([live_host.LiveHost]) - def get_all(self): - """Returns all hosts.""" - handler = live_host_handler.HostHandler(pecan.request) - hosts = handler.get_all() - return hosts - - @util.policy_enforce(['authenticated']) - @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() - def _lookup(self, host_name, *remainder): - return HostController(host_name), remainder - - -class ConfigController(rest.RestController): - - @util.policy_enforce(['authenticated']) - @pecan.expose() - def get_all(self): - """Returns config from a specific host.""" - return "Dump CONFIG" - - -class HostServicesController(rest.RestController): - - @pecan.expose() - def _lookup(self, service_name, *remainder): - return HostServiceController(service_name), remainder - - -class HostServiceMetricsController(rest.RestController): - - @util.policy_enforce(['authenticated']) - @wsme_pecan.wsexpose([live_metric.LiveMetric]) - def get(self): - """Returns all metrics name for a host with a service.""" - handler = live_metric_handler.MetricHandler(pecan.request) - metrics_name = handler.get( - pecan.request.context['host_name'], - service_description=pecan.request.context['service_name'] - ) - return metrics_name - - @pecan.expose() - def _lookup(self, metric_name, *remainder): - return HostServiceMetricController(metric_name), remainder - - -class HostMetricsController(rest.RestController): - - @util.policy_enforce(['authenticated']) - @wsme_pecan.wsexpose([live_metric.LiveMetric]) - def get(self): - """Returns all metrics name for a host.""" - handler = live_metric_handler.MetricHandler(pecan.request) - metrics_name = handler.get(pecan.request.context['host_name']) - return metrics_name - - @pecan.expose() - def _lookup(self, metric_name, *remainder): - return HostMetricController(metric_name), remainder - - -class HostCheckResultsSubController(rest.RestController): - - @util.policy_enforce(['authenticated']) - @wsme_pecan.wsexpose(body=checkresult.CheckResult, status_code=204) - def post(self, data): - """Submit a new check result. - - :param data: a check result within the request body. - """ - result = data.as_dict() - result['host_name'] = pecan.request.context['host_name'] - - requests.post( - pecan.request.ws_arbiter_url + "/push_check_result", - data=result - ) - - -class ServiceCheckResultsSubController(rest.RestController): - - @util.policy_enforce(['authenticated']) - @wsme_pecan.wsexpose(body=checkresult.CheckResult, status_code=204) - def post(self, data): - """Submit a new check result. - - :param data: a check result within the request body. - """ - result = data.as_dict() - result['host_name'] = pecan.request.context['host_name'] - - result['service_description'] = pecan.request.context[ - 'service_name' - ] - - requests.post( - pecan.request.ws_arbiter_url + "/push_check_result", - data=result - ) - - -class HostServiceController(rest.RestController): - - metrics = HostServiceMetricsController() - results = ServiceCheckResultsSubController() - - def __init__(self, service_name): - pecan.request.context['service_name'] = service_name - self.service_name = service_name - - @util.policy_enforce(['authenticated']) - @wsme_pecan.wsexpose(live_service.LiveService) - def get(self): - """Returns a specific host service.""" - handler = live_service_handler.ServiceHandler(pecan.request) - service = handler.get( - pecan.request.context['host_name'], - self.service_name - ) - return service - - -class HostServiceMetricController(rest.RestController): - - def __init__(self, metric_name): - pecan.request.context['metric_name'] = metric_name - self.metric_name = metric_name - - @util.policy_enforce(['authenticated']) - @wsme_pecan.wsexpose(live_metric.LiveMetric) - def get(self): - """Return the last measure for the metric name - - of the service name on the host name. - """ - handler = live_metric_handler.MetricHandler(pecan.request) - metric = handler.get( - pecan.request.context['host_name'], - self.metric_name, - pecan.request.context['service_name'] - ) - return metric - - @util.policy_enforce(['authenticated']) - @wsme_pecan.wsexpose([live_metric.LiveMetric], body=live_query.LiveQuery) - def post(self, query): - """Returns all matching metrics. - - :param time: a time delta within the request body. - """ - handler = live_metric_handler.MetricHandler(pecan.request) - metrics = handler.get_all(metric_name=self.metric_name, - host_name=pecan.request.context['host_name'], - service_description=pecan.request - .context['service_name'], - live_query=query) - return metrics - - -class HostMetricController(rest.RestController): - - def __init__(self, metric_name): - pecan.request.context['metric_name'] = metric_name - self.metric_name = metric_name - - @util.policy_enforce(['authenticated']) - @wsme_pecan.wsexpose(live_metric.LiveMetric) - def get(self): - """Return the last measure for the metric name - - of the service name on the host name - """ - handler = live_metric_handler.MetricHandler(pecan.request) - metric = handler.get( - pecan.request.context['host_name'], - self.metric_name - ) - return metric - - @util.policy_enforce(['authenticated']) - @wsme_pecan.wsexpose([live_metric.LiveMetric], body=live_query.LiveQuery) - def post(self, query): - """Given a LiveQuery, returns all matching metrics. - - :param time: a live query within the request body. - """ - handler = live_metric_handler.MetricHandler(pecan.request) - metrics = handler.get_all(metric_name=self.metric_name, - host_name=pecan.request.context['host_name'], - live_query=query) - return metrics - - -class HostController(rest.RestController): - - services = HostServicesController() - # See init for controller creation. We need host_name to instanciate it - # externalcommands = ExternalCommandsController() - # config = config.ConfigController() - events = events.EventsController() - metrics = HostMetricsController() - results = HostCheckResultsSubController() - - def __init__(self, host_name): - pecan.request.context['host_name'] = host_name - self.host_name = host_name - - @util.policy_enforce(['authenticated']) - @wsme_pecan.wsexpose(live_host.LiveHost) - def get(self): - """Returns a specific host.""" - handler = live_host_handler.HostHandler(pecan.request) - host = handler.get(self.host_name) - return host diff --git a/surveil/api/controllers/v2/status/hosts/__init__.py b/surveil/api/controllers/v2/status/hosts/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/surveil/api/controllers/v2/status/hosts/hosts.py b/surveil/api/controllers/v2/status/hosts/hosts.py new file mode 100644 index 0000000..e588cdf --- /dev/null +++ b/surveil/api/controllers/v2/status/hosts/hosts.py @@ -0,0 +1,80 @@ +# 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.controllers.v2.status.hosts import metrics as host_metrics +from surveil.api.controllers.v2.status.hosts import results as host_results +from surveil.api.controllers.v2.status.hosts.services import ( + services as host_services) +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 +from surveil.common import util + + +class HostsController(rest.RestController): + + @util.policy_enforce(['authenticated']) + @wsme_pecan.wsexpose([live_host.LiveHost]) + def get_all(self): + """Returns all hosts.""" + handler = live_host_handler.HostHandler(pecan.request) + hosts = handler.get_all() + return hosts + + @util.policy_enforce(['authenticated']) + @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() + def _lookup(self, host_name, *remainder): + return HostController(host_name), remainder + + +class HostController(rest.RestController): + + # See init for controller creation. We need host_name to instanciate it + # externalcommands = ExternalCommandsController() + # config = config.ConfigController() + services = host_services.ServicesController() + metrics = host_metrics.MetricsController() + results = host_results.CheckResultsSubController() + + def __init__(self, host_name): + pecan.request.context['host_name'] = host_name + self.host_name = host_name + + @util.policy_enforce(['authenticated']) + @wsme_pecan.wsexpose(live_host.LiveHost) + def get(self): + """Returns a specific host.""" + handler = live_host_handler.HostHandler(pecan.request) + host = handler.get(self.host_name) + return host + + +class ConfigController(rest.RestController): + + @util.policy_enforce(['authenticated']) + @pecan.expose() + def get_all(self): + """Returns config from a specific host.""" + return "Dump CONFIG" diff --git a/surveil/api/controllers/v2/status/hosts/metrics.py b/surveil/api/controllers/v2/status/hosts/metrics.py new file mode 100644 index 0000000..517f807 --- /dev/null +++ b/surveil/api/controllers/v2/status/hosts/metrics.py @@ -0,0 +1,70 @@ +# 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 pecan +from pecan import rest +import wsmeext.pecan as wsme_pecan + +from surveil.api.datamodel.status import live_query +from surveil.api.datamodel.status.metrics import live_metric +from surveil.api.handlers.status.metrics import live_metric_handler +from surveil.api.handlers.status.metrics import metric_name_handler +from surveil.common import util + + +class MetricsController(rest.RestController): + + @util.policy_enforce(['authenticated']) + @wsme_pecan.wsexpose([live_metric.LiveMetric]) + def get(self): + """Returns all metrics name for a host.""" + handler = metric_name_handler.MetricNameHandler(pecan.request) + metrics_name = handler.get(pecan.request.context['host_name']) + return metrics_name + + @pecan.expose() + def _lookup(self, metric_name, *remainder): + return MetricController(metric_name), remainder + + +class MetricController(rest.RestController): + + def __init__(self, metric_name): + pecan.request.context['metric_name'] = metric_name + self.metric_name = metric_name + + @util.policy_enforce(['authenticated']) + @wsme_pecan.wsexpose(live_metric.LiveMetric) + def get(self): + """Return the last measure for the metric name on the host.""" + handler = live_metric_handler.MetricHandler(pecan.request) + metric = handler.get( + pecan.request.context['host_name'], + self.metric_name + ) + return metric + + @util.policy_enforce(['authenticated']) + @wsme_pecan.wsexpose([live_metric.LiveMetric], body=live_query.LiveQuery) + def post(self, query): + """Given a live query, returns all matching metrics. + + :param live_query: a live query within the request body. + """ + handler = live_metric_handler.MetricHandler(pecan.request) + metrics = handler.get_all(live_query=query, + metric_name=self.metric_name, + host_name=pecan.request.context['host_name'] + ) + return metrics diff --git a/surveil/api/controllers/v2/status/hosts/results.py b/surveil/api/controllers/v2/status/hosts/results.py new file mode 100644 index 0000000..d7559a2 --- /dev/null +++ b/surveil/api/controllers/v2/status/hosts/results.py @@ -0,0 +1,39 @@ +# 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 pecan +from pecan import rest +import requests +import wsmeext.pecan as wsme_pecan + +from surveil.api.datamodel import checkresult +from surveil.common import util + + +class CheckResultsSubController(rest.RestController): + + @util.policy_enforce(['authenticated']) + @wsme_pecan.wsexpose(body=checkresult.CheckResult, status_code=204) + def post(self, data): + """Submit a new check result. + + :param data: a check result within the request body. + """ + result = data.as_dict() + result['host_name'] = pecan.request.context['host_name'] + + requests.post( + pecan.request.ws_arbiter_url + "/push_check_result", + data=result + ) diff --git a/surveil/api/controllers/v2/status/hosts/services/__init__.py b/surveil/api/controllers/v2/status/hosts/services/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/surveil/api/controllers/v2/status/hosts/services/metrics.py b/surveil/api/controllers/v2/status/hosts/services/metrics.py new file mode 100644 index 0000000..5fcfe51 --- /dev/null +++ b/surveil/api/controllers/v2/status/hosts/services/metrics.py @@ -0,0 +1,75 @@ +# 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.metrics import live_metric +from surveil.api.handlers.status.metrics import live_metric_handler +from surveil.api.handlers.status.metrics import metric_name_handler +from surveil.common import util + + +class MetricsController(rest.RestController): + + @util.policy_enforce(['authenticated']) + @wsme_pecan.wsexpose([live_metric.LiveMetric]) + def get(self): + """Returns all metrics name for a host with a service.""" + handler = metric_name_handler.MetricNameHandler(pecan.request) + metrics_name = handler.get( + pecan.request.context['host_name'], + pecan.request.context['service_name'] + ) + return metrics_name + + @pecan.expose() + def _lookup(self, metric_name, *remainder): + return MetricController(metric_name), remainder + + +class MetricController(rest.RestController): + + def __init__(self, metric_name): + pecan.request.context['metric_name'] = metric_name + self.metric_name = metric_name + + @util.policy_enforce(['authenticated']) + @wsme_pecan.wsexpose(live_metric.LiveMetric) + def get(self): + """Return the last measure of the metric of the service of the host.""" + handler = live_metric_handler.MetricHandler(pecan.request) + metric = handler.get( + pecan.request.context['host_name'], + self.metric_name, + pecan.request.context['service_name'] + ) + return metric + + @util.policy_enforce(['authenticated']) + @wsme_pecan.wsexpose([live_metric.LiveMetric], body=live_query.LiveQuery) + def post(self, query): + """Returns all matching metrics. + + :param live query: a live query + """ + handler = live_metric_handler.MetricHandler(pecan.request) + metrics = handler.get_all(live_query=query, + metric_name=self.metric_name, + host_name=pecan.request.context['host_name'], + service_description=pecan.request. + context['service_name']) + return metrics diff --git a/surveil/api/controllers/v2/status/hosts/services/results.py b/surveil/api/controllers/v2/status/hosts/services/results.py new file mode 100644 index 0000000..4b81134 --- /dev/null +++ b/surveil/api/controllers/v2/status/hosts/services/results.py @@ -0,0 +1,43 @@ +# 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 requests +import wsmeext.pecan as wsme_pecan + +from surveil.api.datamodel import checkresult +from surveil.common import util + + +class CheckResultsSubController(rest.RestController): + + @util.policy_enforce(['authenticated']) + @wsme_pecan.wsexpose(body=checkresult.CheckResult, status_code=204) + def post(self, data): + """Submit a new check result. + + :param data: a check result within the request body. + """ + result = data.as_dict() + result['host_name'] = pecan.request.context['host_name'] + + result['service_description'] = pecan.request.context[ + 'service_name' + ] + + requests.post( + pecan.request.ws_arbiter_url + "/push_check_result", + data=result + ) diff --git a/surveil/api/controllers/v2/status/hosts/services/services.py b/surveil/api/controllers/v2/status/hosts/services/services.py new file mode 100644 index 0000000..b7edb43 --- /dev/null +++ b/surveil/api/controllers/v2/status/hosts/services/services.py @@ -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 pecan +from pecan import rest +import wsmeext.pecan as wsme_pecan + +from surveil.api.controllers.v2.status.hosts.services import ( + metrics as services_metrics) +from surveil.api.controllers.v2.status.hosts.services import ( + results as services_check_results) +from surveil.api.datamodel.status import live_service +from surveil.api.handlers.status import live_service_handler +from surveil.common import util + + +class ServicesController(rest.RestController): + + @pecan.expose() + def _lookup(self, service_name, *remainder): + return ServiceController(service_name), remainder + + +class ServiceController(rest.RestController): + + metrics = services_metrics.MetricsController() + results = services_check_results.CheckResultsSubController() + + def __init__(self, service_name): + pecan.request.context['service_name'] = service_name + self.service_name = service_name + + @util.policy_enforce(['authenticated']) + @wsme_pecan.wsexpose(live_service.LiveService) + def get(self): + """Returns a specific host service.""" + handler = live_service_handler.ServiceHandler(pecan.request) + service = handler.get( + pecan.request.context['host_name'], + self.service_name + ) + return service diff --git a/surveil/api/controllers/v2/status/services/__init__.py b/surveil/api/controllers/v2/status/services/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/surveil/api/controllers/v2/status/services.py b/surveil/api/controllers/v2/status/services/services.py similarity index 100% rename from surveil/api/controllers/v2/status/services.py rename to surveil/api/controllers/v2/status/services/services.py diff --git a/surveil/api/controllers/v2/status/status.py b/surveil/api/controllers/v2/status/status.py new file mode 100644 index 0000000..ee4ca8e --- /dev/null +++ b/surveil/api/controllers/v2/status/status.py @@ -0,0 +1,25 @@ +# 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 pecan import rest + +from surveil.api.controllers.v2.status.events import events as v2_events +from surveil.api.controllers.v2.status.hosts import hosts as v2_hosts +from surveil.api.controllers.v2.status.services import services as v2_services + + +class StatusController(rest.RestController): + hosts = v2_hosts.HostsController() + services = v2_services.ServicesController() + events = v2_events.EventsController() diff --git a/surveil/api/controllers/v2/v2.py b/surveil/api/controllers/v2/v2.py index 44743ad..913e81e 100644 --- a/surveil/api/controllers/v2/v2.py +++ b/surveil/api/controllers/v2/v2.py @@ -18,7 +18,7 @@ from surveil.api.controllers.v2 import auth as v2_auth from surveil.api.controllers.v2 import bansho as v2_bansho from surveil.api.controllers.v2 import config as v2_config from surveil.api.controllers.v2 import hello as v2_hello -from surveil.api.controllers.v2 import status as v2_status +from surveil.api.controllers.v2.status import status as v2_status class V2Controller(object): diff --git a/surveil/api/handlers/status/metrics/live_metric_handler.py b/surveil/api/handlers/status/metrics/live_metric_handler.py index 51aa4a7..bc3e02a 100644 --- a/surveil/api/handlers/status/metrics/live_metric_handler.py +++ b/surveil/api/handlers/status/metrics/live_metric_handler.py @@ -21,11 +21,8 @@ from surveil.api.handlers.status import influxdb_query class MetricHandler(handler.Handler): """Fulfills a request on the metrics.""" - def get(self, host_name, metric_name=None, service_description=None): - """Return metrics name if param metric_name is null, - - else , return the last metric. - """ + def get(self, host_name, metric_name, service_description=None): + """Return the last metric.""" metrics = [] cli = self.request.influxdb_client if metric_name is None: @@ -110,20 +107,15 @@ class MetricHandler(handler.Handler): return metrics def _metric_dict_from_influx_item(self, item, metric_name=None): - - if metric_name is None: - metric_dict = None - mappings = [('name', 'metric_name', str), ] - else: - metric_dict = {"metric_name": str(metric_name)} - mappings = [ - ('min', str), - ('max', str), - ('critical', str), - ('warning', str), - ('value', str), - ('unit', str), - ] + metric_dict = {"metric_name": str(metric_name)} + mappings = [ + ('min', str), + ('max', str), + ('critical', str), + ('warning', str), + ('value', str), + ('unit', str), + ] for field in mappings: value = item.get(field[0], None) @@ -136,14 +128,3 @@ class MetricHandler(handler.Handler): metric_dict[field[0]] = field[1](value) return metric_dict - - def _metrics_name_from_influx_item(self, item): - - metric_name = {} - mappings = [('metric_name', 'name', str), ] - for field in mappings: - value = item.get(field[1], None) - if value is not None: - metric_name[field[0]] = field[2](value) - - return metric_name diff --git a/surveil/api/handlers/status/metrics/metric_name_handler.py b/surveil/api/handlers/status/metrics/metric_name_handler.py new file mode 100644 index 0000000..a57eef2 --- /dev/null +++ b/surveil/api/handlers/status/metrics/metric_name_handler.py @@ -0,0 +1,56 @@ +# 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 surveil.api.datamodel.status.metrics import live_metric +from surveil.api.handlers import handler + + +class MetricNameHandler(handler.Handler): + """Fulfills a request on the metrics.""" + + def get(self, host_name, service_description=None): + """Return all metrics name.""" + metrics = [] + cli = self.request.influxdb_client + + if service_description is None: + query = ("SHOW measurements WHERE host_name='%s' " + "AND service_description=''" + % host_name) + else: + query = ("SHOW measurements WHERE host_name='%s' " + "AND service_description='%s'" + % (host_name, service_description)) + + response = cli.query(query) + + for item in response[None]: + metric_dict = self._metrics_name_from_influx_item(item) + if metric_dict is not None: + metrics.append(live_metric.LiveMetric(**metric_dict)) + + return metrics + + def _metrics_name_from_influx_item(self, item): + + metric_name = None + mappings = [('metric_name', 'name', str), ] + for field in mappings: + value = item.get(field[1], None) + if value is not None: + if value.startswith('metric_'): + metric_name = {} + metric_name[field[0]] = field[2](value[7:]) + + return metric_name diff --git a/surveil/tests/api/controllers/v2/status/test_hosts_metric.py b/surveil/tests/api/controllers/v2/status/test_hosts_metric.py index 34366b5..92b28e7 100644 --- a/surveil/tests/api/controllers/v2/status/test_hosts_metric.py +++ b/surveil/tests/api/controllers/v2/status/test_hosts_metric.py @@ -113,77 +113,6 @@ class TestHostMetric(functionalTest.FunctionalTest): "order by time desc limit 1"] ) - def test_time_hosts(self): - self.influxdb_response = json.dumps({ - "results": [ - { - "series": [ - {"name": "metric_load1", - "tags": {"host_name": "srv-monitoring-01", - "service_description": "load"}, - "columns": ["time", - "critical", - "min", - "value", - "warning", - ], - "values": [["2015-04-19T01:09:24Z", - "30", - "0", - "0.6", - "15"], - ["2015-04-19T01:09:25Z", - "40", - "4", - "10", - "10"]]}]}] - - }) - - with requests_mock.Mocker() as m: - m.register_uri(requests_mock.GET, - "http://influxdb:8086/query", - text=self.influxdb_response) - - query = { - 'fields': [], - 'time_interval': { - 'start_time': '2015-04-19T00:09:24Z', - 'end_time': '2015-04-19T02:09:25Z' - } - } - - response = self.post_json("/v2/status/hosts/srv-monitoring-01/" - "services/load/metrics/load1", - params=query) - - expected = [{"metric_name": 'load1', - "min": "0", - "critical": "30", - "warning": "15", - "value": "0.6" - }, - {"metric_name": 'load1', - "min": "4", - "critical": "40", - "warning": "10", - "value": "10" - }] - - self.assertEqual( - m.last_request.qs['q'], - ["select * from metric_load1 " - "where time >= '2015-04-19t00:09:24z' " - "and time <= '2015-04-19t02:09:25z' " - "and host_name='srv-monitoring-01' " - "and service_description='load' " - "order by time desc" - ] - ) - self.assert_count_equal_backport( - json.loads(response.body.decode()), - expected) - def test_metric_names(self): self.influxdb_response = json.dumps({ "results": [ @@ -220,40 +149,3 @@ class TestHostMetric(functionalTest.FunctionalTest): ["show measurements where host_name='ws-arbiter' " "and service_description=''"] ) - - def test_metric_names_services(self): - self.influxdb_response = json.dumps({ - "results": [ - { - "series": [ - { - "name": "measurements", - "columns": ["name"], - "values": [ - ["metric_rtmin"], - ["ALERT"] - ] - } - ] - } - ] - }) - with requests_mock.Mocker() as m: - m.register_uri(requests_mock.GET, - "http://influxdb:8086/query", - text=self.influxdb_response) - - response = self.get( - "/v2/status/hosts/localhost/services/load/metrics" - ) - - expected = [{"metric_name": "rtmin"}, ] - - self.assert_count_equal_backport( - json.loads(response.body.decode()), - expected) - self.assertEqual( - m.last_request.qs['q'], - ["show measurements where host_name='localhost' " - "and service_description='load'"] - ) diff --git a/surveil/tests/api/controllers/v2/status/test_hosts_services_metrics.py b/surveil/tests/api/controllers/v2/status/test_hosts_services_metrics.py new file mode 100644 index 0000000..f396bac --- /dev/null +++ b/surveil/tests/api/controllers/v2/status/test_hosts_services_metrics.py @@ -0,0 +1,233 @@ +# 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 requests_mock + +from surveil.tests.api import functionalTest + + +class TestHostMetric(functionalTest.FunctionalTest): + + def setUp(self): + super(TestHostMetric, self).setUp() + self.influxdb_response = json.dumps({ + "results": [ + { + "series": [ + {"name": "metric_load1", + "tags": {"host_name": "srv-monitoring-01", + "service_description": "load", + }, + "columns": [ + "time", + "critical", + "min", + "value", + "warning" + ], + "values": [ + ["2015-04-19T01:09:24Z", + "30", + "0", + "0.60", + "15"] + ]}, + {"name": "metric_load1", + "tags": {"host_name": "test_keystone", + "service_description": "unload"}, + "columns": [ + "time", + "critical", + "min", + "value", + "warning" + ], + "values": [ + ["2015-04-19T01:09:23Z", + "60", + "0", + "1.5", + "20"] + ]}, + {"name": "metric_load1", + "tags": {"host_name": "ws-arbiter", + }, + "columns": [ + "time", + "critical", + "min", + "value", + "warning" + ], + "values": [ + ["2015-04-19T01:09:24Z", + "20", + "0", + "6", + "10"], + ]} + ] + } + ] + }) + + def test_get(self): + """Test get all metric names for a service.""" + self.influxdb_response = json.dumps({ + "results": [ + { + "series": [ + { + "name": "measurements", + "columns": ["name"], + "values": [ + ["metric_rtmin"], + ["ALERT"] + ] + } + ] + } + ] + }) + with requests_mock.Mocker() as m: + m.register_uri(requests_mock.GET, + "http://influxdb:8086/query", + text=self.influxdb_response) + + response = self.get( + "/v2/status/hosts/localhost/services/load/metrics" + ) + + expected = [{"metric_name": "rtmin"}, ] + + self.assert_count_equal_backport( + json.loads(response.body.decode()), + expected) + self.assertEqual( + m.last_request.qs['q'], + ["show measurements where host_name='localhost' " + "and service_description='load'"] + ) + + def test_get_metric_name(self): + """Test get the last measure for a metric within a service.""" + self.influxdb_response = json.dumps({ + "results": [ + { + "series": [ + { + "name": "measurements", + "columns": ["name"], + "values": [ + ["metric_rtmin"], + ["ALERT"] + ] + } + ] + } + ] + }) + with requests_mock.Mocker() as m: + m.register_uri(requests_mock.GET, + "http://influxdb:8086/query", + text=self.influxdb_response) + + response = self.get( + "/v2/status/hosts/ws-arbiter/services/load/metrics" + ) + + expected = [{"metric_name": "rtmin"}, ] + + self.assert_count_equal_backport( + json.loads(response.body.decode()), + expected) + self.assertEqual( + m.last_request.qs['q'], + ["show measurements where host_name='ws-arbiter' " + "and service_description='load'"] + ) + + def test_post_live_query(self): + """Test posting a live query.""" + self.influxdb_response = json.dumps({ + "results": [ + { + "series": [ + {"name": "metric_load1", + "tags": {"host_name": "srv-monitoring-01", + "service_description": "load"}, + "columns": ["time", + "critical", + "min", + "value", + "warning", + ], + "values": [["2015-04-19T01:09:24Z", + "30", + "0", + "0.6", + "15"], + ["2015-04-19T01:09:25Z", + "40", + "4", + "10", + "10"]]}]}] + + }) + + with requests_mock.Mocker() as m: + m.register_uri(requests_mock.GET, + "http://influxdb:8086/query", + text=self.influxdb_response) + + query = { + 'fields': [], + 'time_interval': { + 'start_time': '2015-04-19T00:09:24Z', + 'end_time': '2015-04-19T02:09:25Z' + } + } + + response = self.post_json("/v2/status/hosts/srv-monitoring-01/" + "services/load/metrics/load1", + params=query) + + expected = [{"metric_name": 'load1', + "min": "0", + "critical": "30", + "warning": "15", + "value": "0.6" + }, + {"metric_name": 'load1', + "min": "4", + "critical": "40", + "warning": "10", + "value": "10" + }] + + self.assertEqual( + m.last_request.qs['q'], + ["select * from metric_load1 " + "where time >= '2015-04-19t00:09:24z' " + "and time <= '2015-04-19t02:09:25z' " + "and host_name='srv-monitoring-01' " + "and service_description='load' " + "order by time desc" + ] + ) + self.assert_count_equal_backport( + json.loads(response.body.decode()), + expected)