From f4be811794c6abebf21732a21038fee7116f77e6 Mon Sep 17 00:00:00 2001
From: Vincent Fournier <v.fournier11@gmail.com>
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)