From 5212c8021cf13e58ffff77694a81cc7553c91817 Mon Sep 17 00:00:00 2001 From: Eyal Date: Mon, 1 Jul 2019 12:55:49 +0300 Subject: [PATCH] Add new api vitrage status To Check if vitrage is ready then you can check its status some users don't want to check on every api call if vitrage is ready, in that case you can check ad hoc Change-Id: Icf39361ae47bc132bab760507967907f34b81821 --- doc/source/contributor/vitrage-api.rst | 68 ++++++++++++++++++- .../notes/add_status-c5b13941c3ce978e.yaml | 3 + vitrage/api/__init__.py | 2 +- vitrage/api/controllers/v1/root.py | 2 + vitrage/api/controllers/v1/service.py | 3 +- vitrage/api/controllers/v1/status.py | 38 +++++++++++ vitrage/common/policies/__init__.py | 2 + vitrage/common/policies/status.py | 37 ++++++++++ .../tests/functional/api/v1/test_status.py | 64 +++++++++++++++++ 9 files changed, 216 insertions(+), 3 deletions(-) create mode 100644 releasenotes/notes/add_status-c5b13941c3ce978e.yaml create mode 100644 vitrage/api/controllers/v1/status.py create mode 100644 vitrage/common/policies/status.py create mode 100644 vitrage/tests/functional/api/v1/test_status.py diff --git a/doc/source/contributor/vitrage-api.rst b/doc/source/contributor/vitrage-api.rst index 3ee2a547b..c1fc507d0 100644 --- a/doc/source/contributor/vitrage-api.rst +++ b/doc/source/contributor/vitrage-api.rst @@ -2001,4 +2001,70 @@ Response Examples "version": "v3", "status": "CURRENT" } - ] \ No newline at end of file + ] + + + +Status +^^^^^^ + +prints the status of vitrage + +GET /v1/status +~~~~~~~~~~~~~~~ + +Headers +======= + +- X-Auth-Token (string, required) - Keystone auth token +- Accept (string) - application/json + +Path Parameters +=============== + +None. + +Query Parameters +================ + +None. + +Request Body +============ + +None. + +Request Examples +================ + +:: + + GET //v1/status HTTP/1.1 + Host: 135.248.19.18:8999 + X-Auth-Token: 2b8882ba2ec44295bf300aecb2caa4f7 + Accept: application/json + + + +ResponseStatus code +=================== + +- 200 - OK +- 503 - Service Unavailable vitrage-graph is not ready +- 503 - Service Unavailable vitrage-graph is not available + +Response Body +============= + +Returns a JSON object with ok status +or 503 status code if not ok + +Response Examples +================= + +:: + + { + "reason": "OK" + } + diff --git a/releasenotes/notes/add_status-c5b13941c3ce978e.yaml b/releasenotes/notes/add_status-c5b13941c3ce978e.yaml new file mode 100644 index 000000000..c2a9085c2 --- /dev/null +++ b/releasenotes/notes/add_status-c5b13941c3ce978e.yaml @@ -0,0 +1,3 @@ +--- +features: + - Added a new API to show vitrage status. \ No newline at end of file diff --git a/vitrage/api/__init__.py b/vitrage/api/__init__.py index 822c3d95c..0a404cee6 100644 --- a/vitrage/api/__init__.py +++ b/vitrage/api/__init__.py @@ -30,5 +30,5 @@ OPTS = [ 'keycloak'}, help='Authentication mode to use.'), cfg.BoolOpt('check_backend', default=True, - help='Check that that backend is ready before an api call') + help='Check that backend is ready before an api call') ] diff --git a/vitrage/api/controllers/v1/root.py b/vitrage/api/controllers/v1/root.py index 29c51f41a..ce0df26fa 100644 --- a/vitrage/api/controllers/v1/root.py +++ b/vitrage/api/controllers/v1/root.py @@ -16,6 +16,7 @@ from vitrage.api.controllers.v1 import event from vitrage.api.controllers.v1 import rca from vitrage.api.controllers.v1 import resource from vitrage.api.controllers.v1 import service +from vitrage.api.controllers.v1 import status from vitrage.api.controllers.v1 import template from vitrage.api.controllers.v1 import topology from vitrage.api.controllers.v1 import webhook @@ -33,3 +34,4 @@ class V1Controller(object): template = template.TemplateController() event = event.EventController() service = service.ServiceController() + status = status.StatusController() diff --git a/vitrage/api/controllers/v1/service.py b/vitrage/api/controllers/v1/service.py index fb6790d10..229ab632d 100644 --- a/vitrage/api/controllers/v1/service.py +++ b/vitrage/api/controllers/v1/service.py @@ -18,13 +18,14 @@ from oslo_log import log import pecan from pecan.core import abort +from vitrage.api.controllers.rest import RootRestController from vitrage.api.policy import enforce LOG = log.getLogger(__name__) # noinspection PyBroadException -class ServiceController(object): +class ServiceController(RootRestController): @pecan.expose('json') def index(self): return self.get() diff --git a/vitrage/api/controllers/v1/status.py b/vitrage/api/controllers/v1/status.py new file mode 100644 index 000000000..c30d41b50 --- /dev/null +++ b/vitrage/api/controllers/v1/status.py @@ -0,0 +1,38 @@ +# Copyright 2019 - Nokia Corporation +# +# 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 oslo_messaging +import pecan +from pecan import rest +from vitrage.api.policy import enforce + + +class StatusController(rest.RestController): + @pecan.expose('json') + def index(self): + return self.get() + + @pecan.expose('json') + def get(self): + enforce("get status", pecan.request.headers, + pecan.request.enforcer, {}) + try: + client = pecan.request.client.prepare(timeout=5) + backend_is_alive = client.call(pecan.request.context, 'is_alive') + if backend_is_alive: + return {'reason': 'OK'} + else: + pecan.abort(503, detail='vitrage-graph is not ready') + except oslo_messaging.MessagingTimeout: + pecan.abort(503, detail='vitrage-graph is not available') diff --git a/vitrage/common/policies/__init__.py b/vitrage/common/policies/__init__.py index efcafda2a..51630c232 100644 --- a/vitrage/common/policies/__init__.py +++ b/vitrage/common/policies/__init__.py @@ -17,6 +17,7 @@ from vitrage.common.policies import event from vitrage.common.policies import rca from vitrage.common.policies import resource from vitrage.common.policies import service +from vitrage.common.policies import status from vitrage.common.policies import template from vitrage.common.policies import topology from vitrage.common.policies import webhook @@ -32,4 +33,5 @@ def list_rules(): resource.list_rules(), webhook.list_rules(), service.list_rules(), + status.list_rules(), ) diff --git a/vitrage/common/policies/status.py b/vitrage/common/policies/status.py new file mode 100644 index 000000000..b6f7d7b7a --- /dev/null +++ b/vitrage/common/policies/status.py @@ -0,0 +1,37 @@ +# Copyright 2019 - Nokia Corporation +# +# 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 oslo_policy import policy + +from vitrage.common.policies import base + +STATUS = 'get status' + +rules = [ + policy.DocumentedRuleDefault( + name=STATUS, + check_str=base.UNPROTECTED, + description='Get the vitrage status for the OpenStack cluster', + operations=[ + { + 'path': '/status', + 'method': 'GET' + } + ] + ) +] + + +def list_rules(): + return rules diff --git a/vitrage/tests/functional/api/v1/test_status.py b/vitrage/tests/functional/api/v1/test_status.py new file mode 100644 index 000000000..baaf884af --- /dev/null +++ b/vitrage/tests/functional/api/v1/test_status.py @@ -0,0 +1,64 @@ +# Copyright 2019 - Nokia Corporation +# +# 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. +# +# 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 mock + +import oslo_messaging + + +from vitrage.tests.functional.api.v1 import FunctionalTest + + +class StatusTest(FunctionalTest): + def __init__(self, *args, **kwds): + super(StatusTest, self).__init__(*args, **kwds) + self.auth = 'noauth' + + def test_get_status_ok(self): + with mock.patch('pecan.request') as request: + client = mock.Mock() + client.call.return_value = True + request.client.prepare.return_value = client + resp = self.get_json('/status/') + self.assert_dict_equal({'reason': 'OK'}, resp) + + def test_get_status_not_ok(self): + with mock.patch('pecan.request') as request: + client = mock.Mock() + client.call.return_value = False + request.client.prepare.return_value = client + resp = self.get_json('/status/', expect_errors=True) + self.assertEqual(503, resp.status_code) + self.assertIn('vitrage-graph is not ready', resp.text) + + def test_get_status_not_ok_timeout(self): + with mock.patch('pecan.request') as request: + client = mock.Mock() + client.call.side_effect = oslo_messaging.MessagingTimeout() + request.client.prepare.return_value = client + resp = self.get_json('/status/', expect_errors=True) + self.assertEqual(503, resp.status_code) + self.assertIn('vitrage-graph is not available', resp.text)