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:
Tyler Smith 2017-08-02 16:08:42 -04:00
parent a54916b695
commit 17b0322679
7 changed files with 211 additions and 7 deletions

62
doc/source/contributor/vitrage-api.rst Normal file → Executable file
View 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
View 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
View 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):

View 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
View 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
View 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
View 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()