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)