Implements alarm counts API
This change is to introduce a new rest api (v1/alarm/count) to vitrage to provide counts of any alarms present in the system. - It can optionally provide counts of alarms for all_tenants similarly to the alarm api - The rest api doc has been updated - Unit test were added to test the api with and without all_tenants as well as noauth Change-Id: I6061b63c068580dcd25df5c624d9b3bd88f30cca Implements: blueprint alarm-counts-api Signed-off-by: Tyler Smith <tyler.smith@windriver.com>
This commit is contained in:
parent
a54916b695
commit
17b0322679
62
doc/source/contributor/vitrage-api.rst
Normal file → Executable file
62
doc/source/contributor/vitrage-api.rst
Normal file → Executable file
@ -858,6 +858,68 @@ Response Examples
|
||||
}
|
||||
]
|
||||
|
||||
Show Alarm Count
|
||||
^^^^^^^^^^^^^^^^
|
||||
|
||||
Shows how many alarms of each operations severity exist
|
||||
|
||||
GET /v1/alarm/count
|
||||
~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Headers
|
||||
=======
|
||||
|
||||
- X-Auth-Token (string, required) - Keystone auth token
|
||||
|
||||
Path Parameters
|
||||
===============
|
||||
|
||||
None.
|
||||
|
||||
Query Parameters
|
||||
================
|
||||
|
||||
None.
|
||||
|
||||
Request Body
|
||||
============
|
||||
|
||||
* all_tenants - (boolean, optional) includes alarms of all tenants in the count (in case the user has the permissions).
|
||||
|
||||
Request Examples
|
||||
================
|
||||
|
||||
::
|
||||
|
||||
GET /v1/alarm/count/ HTTP/1.1
|
||||
Host: 135.248.19.18:8999
|
||||
X-Auth-Token: 2b8882ba2ec44295bf300aecb2caa4f7
|
||||
Accept: application/json
|
||||
|
||||
Response Status code
|
||||
====================
|
||||
|
||||
- 200 - OK
|
||||
|
||||
Response Body
|
||||
=============
|
||||
|
||||
Returns a JSON object with all the alarms requested.
|
||||
|
||||
Response Examples
|
||||
=================
|
||||
|
||||
::
|
||||
|
||||
{
|
||||
"severe": 2,
|
||||
"critical": 1,
|
||||
"warning": 3,
|
||||
"na": 4,
|
||||
"ok": 5
|
||||
}
|
||||
|
||||
|
||||
Template Validate
|
||||
^^^^^^^^^^^^^^^^^
|
||||
|
||||
|
2
etc/vitrage/policy.json
Normal file → Executable file
2
etc/vitrage/policy.json
Normal file → Executable file
@ -6,6 +6,8 @@
|
||||
"list resources:all_tenants": "role:admin",
|
||||
"list alarms": "",
|
||||
"list alarms:all_tenants": "role:admin",
|
||||
"get alarms count": "",
|
||||
"get alarms count:all_tenants": "role:admin",
|
||||
"get rca": "",
|
||||
"get rca:all_tenants": "role:admin",
|
||||
"template validate": "",
|
||||
|
2
vitrage/api/controllers/v1/alarm.py
Normal file → Executable file
2
vitrage/api/controllers/v1/alarm.py
Normal file → Executable file
@ -21,6 +21,7 @@ from osprofiler import profiler
|
||||
from pecan.core import abort
|
||||
|
||||
from vitrage.api.controllers.rest import RootRestController
|
||||
from vitrage.api.controllers.v1 import count
|
||||
from vitrage.api.policy import enforce
|
||||
|
||||
|
||||
@ -30,6 +31,7 @@ LOG = log.getLogger(__name__)
|
||||
@profiler.trace_cls("alarm controller",
|
||||
info={}, hide_args=False, trace_private=False)
|
||||
class AlarmsController(RootRestController):
|
||||
count = count.CountsController()
|
||||
|
||||
@pecan.expose('json')
|
||||
def index(self, vitrage_id, all_tenants=False):
|
||||
|
54
vitrage/api/controllers/v1/count.py
Executable file
54
vitrage/api/controllers/v1/count.py
Executable file
@ -0,0 +1,54 @@
|
||||
# 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 pecan
|
||||
|
||||
from oslo_log import log
|
||||
from oslo_utils.strutils import bool_from_string
|
||||
from pecan.core import abort
|
||||
|
||||
from vitrage.api.controllers.rest import RootRestController
|
||||
from vitrage.api.policy import enforce
|
||||
|
||||
|
||||
LOG = log.getLogger(__name__)
|
||||
|
||||
|
||||
class CountsController(RootRestController):
|
||||
|
||||
@pecan.expose('json')
|
||||
def index(self, all_tenants=False):
|
||||
return self.get(all_tenants)
|
||||
|
||||
@pecan.expose('json')
|
||||
def get(self, all_tenants=False):
|
||||
all_tenants = bool_from_string(all_tenants)
|
||||
if all_tenants:
|
||||
enforce("get alarms count:all_tenants", pecan.request.headers,
|
||||
pecan.request.enforcer, {})
|
||||
else:
|
||||
enforce("get alarms count", pecan.request.headers,
|
||||
pecan.request.enforcer, {})
|
||||
|
||||
LOG.info('received get alarm counts')
|
||||
|
||||
try:
|
||||
alarm_counts_json = pecan.request.client.call(
|
||||
pecan.request.context, 'get_alarm_counts',
|
||||
all_tenants=all_tenants)
|
||||
|
||||
return json.loads(alarm_counts_json)
|
||||
|
||||
except Exception as e:
|
||||
LOG.exception('failed to get alarm counts %s', e)
|
||||
abort(404, str(e))
|
30
vitrage/api_handler/apis/alarm.py
Normal file → Executable file
30
vitrage/api_handler/apis/alarm.py
Normal file → Executable file
@ -21,6 +21,8 @@ from vitrage.api_handler.apis.base import ALARMS_ALL_QUERY
|
||||
from vitrage.api_handler.apis.base import EntityGraphApisBase
|
||||
from vitrage.common.constants import EntityCategory
|
||||
from vitrage.common.constants import VertexProperties as VProps
|
||||
from vitrage.entity_graph.mappings.operational_alarm_severity import \
|
||||
OperationalAlarmSeverity
|
||||
|
||||
|
||||
LOG = log.getLogger(__name__)
|
||||
@ -58,6 +60,34 @@ class AlarmApis(EntityGraphApisBase):
|
||||
|
||||
return json.dumps({'alarms': [v.properties for v in alarms]})
|
||||
|
||||
def get_alarm_counts(self, ctx, all_tenants):
|
||||
LOG.debug("AlarmApis get_alarm_counts - all_tenants=%s", all_tenants)
|
||||
|
||||
project_id = ctx.get(self.TENANT_PROPERTY, None)
|
||||
is_admin_project = ctx.get(self.IS_ADMIN_PROJECT_PROPERTY, False)
|
||||
|
||||
if all_tenants:
|
||||
alarms = self.entity_graph.get_vertices(
|
||||
query_dict=ALARMS_ALL_QUERY)
|
||||
else:
|
||||
alarms = self._get_alarms(project_id, is_admin_project)
|
||||
alarms += self._get_alarms_via_resource(project_id,
|
||||
is_admin_project)
|
||||
alarms = set(alarms)
|
||||
|
||||
counts = {OperationalAlarmSeverity.SEVERE: 0,
|
||||
OperationalAlarmSeverity.CRITICAL: 0,
|
||||
OperationalAlarmSeverity.WARNING: 0,
|
||||
OperationalAlarmSeverity.OK: 0,
|
||||
OperationalAlarmSeverity.NA: 0}
|
||||
|
||||
for alarm in alarms:
|
||||
severity = alarm.get(VProps.VITRAGE_OPERATIONAL_SEVERITY)
|
||||
if severity:
|
||||
counts[severity] += 1
|
||||
|
||||
return json.dumps(counts)
|
||||
|
||||
def _get_alarms(self, project_id, is_admin_project):
|
||||
"""Finds all the alarms with project_id
|
||||
|
||||
|
12
vitrage/tests/functional/api/v1/test_noauth.py
Normal file → Executable file
12
vitrage/tests/functional/api/v1/test_noauth.py
Normal file → Executable file
@ -52,7 +52,6 @@ class NoAuthTest(FunctionalTest):
|
||||
self.assertEqual('200 OK', resp.status)
|
||||
|
||||
def test_noauth_mode_get_topology(self):
|
||||
|
||||
with mock.patch('pecan.request') as request:
|
||||
request.client.call.return_value = '{}'
|
||||
params = dict(depth=None, graph_type='graph', query=None,
|
||||
@ -65,7 +64,6 @@ class NoAuthTest(FunctionalTest):
|
||||
self.assertEqual({}, resp.json)
|
||||
|
||||
def test_noauth_mode_list_alarms(self):
|
||||
|
||||
with mock.patch('pecan.request') as request:
|
||||
request.client.call.return_value = '{"alarms": []}'
|
||||
params = dict(vitrage_id='all', all_tenants=False)
|
||||
@ -75,6 +73,16 @@ class NoAuthTest(FunctionalTest):
|
||||
self.assertEqual('200 OK', resp.status)
|
||||
self.assertEqual([], resp.json)
|
||||
|
||||
def test_noauth_mode_show_alarm_count(self):
|
||||
with mock.patch('pecan.request') as request:
|
||||
request.client.call.return_value = '{}'
|
||||
params = dict(all_tenants=False)
|
||||
resp = self.post_json('/alarm/count/', params=params)
|
||||
|
||||
self.assertEqual(1, request.client.call.call_count)
|
||||
self.assertEqual('200 OK', resp.status)
|
||||
self.assertEqual({}, resp.json)
|
||||
|
||||
def test_noauth_mode_list_resources(self):
|
||||
|
||||
with mock.patch('pecan.request') as request:
|
||||
|
56
vitrage/tests/functional/api_handler/test_apis.py
Normal file → Executable file
56
vitrage/tests/functional/api_handler/test_apis.py
Normal file → Executable file
@ -26,6 +26,8 @@ from vitrage.datasources import NOVA_INSTANCE_DATASOURCE
|
||||
from vitrage.datasources import NOVA_ZONE_DATASOURCE
|
||||
from vitrage.datasources.transformer_base \
|
||||
import create_cluster_placeholder_vertex
|
||||
from vitrage.entity_graph.mappings.operational_alarm_severity import \
|
||||
OperationalAlarmSeverity
|
||||
from vitrage.graph.driver.networkx_graph import NXGraph
|
||||
import vitrage.graph.utils as graph_utils
|
||||
from vitrage.tests.unit.entity_graph.base import TestEntityGraphUnitBase
|
||||
@ -61,6 +63,23 @@ class TestApis(TestEntityGraphUnitBase):
|
||||
self.assertEqual(2, len(alarms))
|
||||
self._check_projects_entities(alarms, 'project_2', True)
|
||||
|
||||
def test_get_alarm_counts_with_not_admin_project(self):
|
||||
# Setup
|
||||
graph = self._create_graph()
|
||||
apis = AlarmApis(graph, None)
|
||||
ctx = {'tenant': 'project_2', 'is_admin': False}
|
||||
|
||||
# Action
|
||||
counts = apis.get_alarm_counts(ctx, all_tenants=False)
|
||||
counts = json.loads(counts)
|
||||
|
||||
# Test assertions
|
||||
self.assertEqual(1, counts['WARNING'])
|
||||
self.assertEqual(0, counts['SEVERE'])
|
||||
self.assertEqual(1, counts['CRITICAL'])
|
||||
self.assertEqual(0, counts['OK'])
|
||||
self.assertEqual(0, counts['N/A'])
|
||||
|
||||
def test_get_alarms_with_all_tenants(self):
|
||||
# Setup
|
||||
graph = self._create_graph()
|
||||
@ -75,6 +94,23 @@ class TestApis(TestEntityGraphUnitBase):
|
||||
self.assertEqual(5, len(alarms))
|
||||
self._check_projects_entities(alarms, None, True)
|
||||
|
||||
def test_get_alarm_counts_with_all_tenants(self):
|
||||
# Setup
|
||||
graph = self._create_graph()
|
||||
apis = AlarmApis(graph, None)
|
||||
ctx = {'tenant': 'project_1', 'is_admin': False}
|
||||
|
||||
# Action
|
||||
counts = apis.get_alarm_counts(ctx, all_tenants=True)
|
||||
counts = json.loads(counts)
|
||||
|
||||
# Test assertions
|
||||
self.assertEqual(2, counts['WARNING'])
|
||||
self.assertEqual(2, counts['SEVERE'])
|
||||
self.assertEqual(1, counts['CRITICAL'])
|
||||
self.assertEqual(0, counts['OK'])
|
||||
self.assertEqual(0, counts['N/A'])
|
||||
|
||||
def test_get_rca_with_admin_project(self):
|
||||
# Setup
|
||||
graph = self._create_graph()
|
||||
@ -418,33 +454,43 @@ class TestApis(TestEntityGraphUnitBase):
|
||||
'alarm_on_host',
|
||||
metadata={VProps.VITRAGE_TYPE: NOVA_HOST_DATASOURCE,
|
||||
VProps.NAME: 'host_1',
|
||||
VProps.RESOURCE_ID: 'host_1'})
|
||||
VProps.RESOURCE_ID: 'host_1',
|
||||
VProps.VITRAGE_OPERATIONAL_SEVERITY:
|
||||
OperationalAlarmSeverity.SEVERE})
|
||||
alarm_on_instance_1_vertex = self._create_alarm(
|
||||
'alarm_on_instance_1',
|
||||
'deduced_alarm',
|
||||
project_id='project_1',
|
||||
metadata={VProps.VITRAGE_TYPE: NOVA_INSTANCE_DATASOURCE,
|
||||
VProps.NAME: 'instance_1',
|
||||
VProps.RESOURCE_ID: 'sdg7849ythksjdg'})
|
||||
VProps.RESOURCE_ID: 'sdg7849ythksjdg',
|
||||
VProps.VITRAGE_OPERATIONAL_SEVERITY:
|
||||
OperationalAlarmSeverity.SEVERE})
|
||||
alarm_on_instance_2_vertex = self._create_alarm(
|
||||
'alarm_on_instance_2',
|
||||
'deduced_alarm',
|
||||
metadata={VProps.VITRAGE_TYPE: NOVA_INSTANCE_DATASOURCE,
|
||||
VProps.NAME: 'instance_2',
|
||||
VProps.RESOURCE_ID: 'nbfhsdugf'})
|
||||
VProps.RESOURCE_ID: 'nbfhsdugf',
|
||||
VProps.VITRAGE_OPERATIONAL_SEVERITY:
|
||||
OperationalAlarmSeverity.WARNING})
|
||||
alarm_on_instance_3_vertex = self._create_alarm(
|
||||
'alarm_on_instance_3',
|
||||
'deduced_alarm',
|
||||
project_id='project_2',
|
||||
metadata={VProps.VITRAGE_TYPE: NOVA_INSTANCE_DATASOURCE,
|
||||
VProps.NAME: 'instance_3',
|
||||
VProps.RESOURCE_ID: 'nbffhsdasdugf'})
|
||||
VProps.RESOURCE_ID: 'nbffhsdasdugf',
|
||||
VProps.VITRAGE_OPERATIONAL_SEVERITY:
|
||||
OperationalAlarmSeverity.CRITICAL})
|
||||
alarm_on_instance_4_vertex = self._create_alarm(
|
||||
'alarm_on_instance_4',
|
||||
'deduced_alarm',
|
||||
metadata={VProps.VITRAGE_TYPE: NOVA_INSTANCE_DATASOURCE,
|
||||
VProps.NAME: 'instance_4',
|
||||
VProps.RESOURCE_ID: 'ngsuy76hgd87f'})
|
||||
VProps.RESOURCE_ID: 'ngsuy76hgd87f',
|
||||
VProps.VITRAGE_OPERATIONAL_SEVERITY:
|
||||
OperationalAlarmSeverity.WARNING})
|
||||
|
||||
# create links
|
||||
edges = list()
|
||||
|
Loading…
x
Reference in New Issue
Block a user