diff --git a/releasenotes/notes/add_alarm_show_api-0b001dd185e2e523.yaml b/releasenotes/notes/add_alarm_show_api-0b001dd185e2e523.yaml new file mode 100644 index 000000000..703195e7d --- /dev/null +++ b/releasenotes/notes/add_alarm_show_api-0b001dd185e2e523.yaml @@ -0,0 +1,4 @@ +--- +features: + - Created a new API to show all alarm properties, with a mandatory parameter + vitrage_id of the alarm. The path for the api is ``/v1/alarm/_id_``. diff --git a/vitrage/api/controllers/v1/alarm.py b/vitrage/api/controllers/v1/alarm.py index 4a590fca4..9a8b5df54 100755 --- a/vitrage/api/controllers/v1/alarm.py +++ b/vitrage/api/controllers/v1/alarm.py @@ -35,11 +35,9 @@ class AlarmsController(RootRestController): count = count.CountsController() @pecan.expose('json') - def index(self, vitrage_id, all_tenants=False): - return self.get(vitrage_id, all_tenants) - - @pecan.expose('json') - def get(self, vitrage_id, all_tenants=False): + def get_all(self, **kwargs): + vitrage_id = kwargs.get('vitrage_id') + all_tenants = kwargs.get('all_tenants', False) all_tenants = bool_from_string(all_tenants) if all_tenants: enforce("list alarms:all_tenants", pecan.request.headers, @@ -73,3 +71,35 @@ class AlarmsController(RootRestController): to_unicode = encodeutils.exception_to_unicode(e) LOG.exception('failed to open file %s ', to_unicode) abort(404, to_unicode) + + @pecan.expose('json') + def get(self, vitrage_id): + enforce("get alarm", + pecan.request.headers, + pecan.request.enforcer, + {}) + + LOG.info('returns show alarm with vitrage id %s', vitrage_id) + + try: + return self._show_alarm(vitrage_id) + except Exception as e: + to_unicode = encodeutils.exception_to_unicode(e) + LOG.exception('failed to load json %s ', to_unicode) + abort(404, to_unicode) + + @staticmethod + def _show_alarm(vitrage_id): + alarm_json = pecan.request.client.call(pecan.request.context, + 'show_alarm', + vitrage_id=vitrage_id) + LOG.info(alarm_json) + + try: + alarms_list = json.loads(alarm_json) + return alarms_list + + except Exception as e: + to_unicode = encodeutils.exception_to_unicode(e) + LOG.exception('failed to load json %s ', to_unicode) + abort(404, to_unicode) diff --git a/vitrage/api_handler/apis/alarm.py b/vitrage/api_handler/apis/alarm.py index c78c8c373..5de7a9cf7 100755 --- a/vitrage/api_handler/apis/alarm.py +++ b/vitrage/api_handler/apis/alarm.py @@ -19,7 +19,7 @@ from osprofiler import profiler from vitrage.api_handler.apis.base import ALARM_QUERY 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 EntityCategory as ECategory from vitrage.common.constants import VertexProperties as VProps from vitrage.entity_graph.mappings.operational_alarm_severity import \ OperationalAlarmSeverity @@ -53,13 +53,30 @@ class AlarmApis(EntityGraphApisBase): is_admin_project) alarms = set(alarms) else: - query = {VProps.VITRAGE_CATEGORY: EntityCategory.ALARM, + query = {VProps.VITRAGE_CATEGORY: ECategory.ALARM, VProps.VITRAGE_IS_DELETED: False} alarms = self.entity_graph.neighbors(vitrage_id, vertex_attr_filter=query) return json.dumps({'alarms': [v.properties for v in alarms]}) + def show_alarm(self, ctx, vitrage_id): + LOG.debug('Show alarm with vitrage_id: %s', vitrage_id) + + alarm = self.entity_graph.get_vertex(vitrage_id) + if not alarm or alarm.get(VProps.VITRAGE_CATEGORY) != ECategory.ALARM: + LOG.warning('Alarm show - not found (%s)', vitrage_id) + return None + + is_admin = ctx.get(self.IS_ADMIN_PROJECT_PROPERTY, False) + curr_project = ctx.get(self.TENANT_PROPERTY, None) + alarm_project = alarm.get(VProps.PROJECT_ID) + if not is_admin and curr_project != alarm_project: + LOG.warning('Authorization failed for alarm (%s)', vitrage_id) + return None + + return json.dumps(alarm.properties) + def get_alarm_counts(self, ctx, all_tenants): LOG.debug("AlarmApis get_alarm_counts - all_tenants=%s", all_tenants) @@ -99,7 +116,7 @@ class AlarmApis(EntityGraphApisBase): :rtype: list """ - alarm_query = self._get_query_with_project(EntityCategory.ALARM, + alarm_query = self._get_query_with_project(ECategory.ALARM, project_id, is_admin_project) alarms = self.entity_graph.get_vertices(query_dict=alarm_query) @@ -117,7 +134,7 @@ class AlarmApis(EntityGraphApisBase): :rtype: list """ - resource_query = self._get_query_with_project(EntityCategory.RESOURCE, + resource_query = self._get_query_with_project(ECategory.RESOURCE, project_id, is_admin_project) diff --git a/vitrage/api_handler/apis/resource.py b/vitrage/api_handler/apis/resource.py index cfdd0e225..a11441f1d 100644 --- a/vitrage/api_handler/apis/resource.py +++ b/vitrage/api_handler/apis/resource.py @@ -58,23 +58,19 @@ class ResourceApis(EntityGraphApisBase): for resource in resources]}) def show_resource(self, ctx, vitrage_id): - LOG.debug('Show resource with vitrage_id: %s', str(vitrage_id)) - - project_id = ctx.get(self.TENANT_PROPERTY, None) - is_admin_project = ctx.get(self.IS_ADMIN_PROJECT_PROPERTY, False) + LOG.debug('Show resource with vitrage_id: %s', vitrage_id) resource = self.entity_graph.get_vertex(vitrage_id) - if resource: - project = resource.get(VProps.PROJECT_ID) - if is_admin_project: - return json.dumps(resource.properties) - else: - if project and project_id == project: - return json.dumps(resource.properties) - LOG.warning( - 'Have no authority to get resource with vitrage_id(%s)', - str(vitrage_id)) - else: - LOG.warning('Can not find the resource with vitrage_id(%s)', - str(vitrage_id)) - return None + if not resource or resource.get(VProps.VITRAGE_CATEGORY) != \ + EntityCategory.RESOURCE: + LOG.warning('Resource show - not found (%s)', vitrage_id) + return None + + is_admin = ctx.get(self.IS_ADMIN_PROJECT_PROPERTY, False) + curr_project = ctx.get(self.TENANT_PROPERTY, None) + resource_project = resource.get(VProps.PROJECT_ID) + if not is_admin and curr_project != resource_project: + LOG.warning('Authorization failed for resource (%s)', vitrage_id) + return None + + return json.dumps(resource.properties) diff --git a/vitrage/common/policies/alarms.py b/vitrage/common/policies/alarms.py index 48bd3579e..4ca552288 100644 --- a/vitrage/common/policies/alarms.py +++ b/vitrage/common/policies/alarms.py @@ -15,6 +15,17 @@ from oslo_policy import policy from vitrage.common.policies import base rules = [ + policy.DocumentedRuleDefault( + name='get alarm', + check_str=base.UNPROTECTED, + description='Show the details of specified alarm', + operations=[ + { + 'path': '/alarm', + 'method': 'GET' + } + ] + ), policy.DocumentedRuleDefault( name='list alarms', check_str=base.UNPROTECTED, diff --git a/vitrage/tests/functional/api/v1/test_noauth.py b/vitrage/tests/functional/api/v1/test_noauth.py index 84691e2c0..3e458cd8c 100755 --- a/vitrage/tests/functional/api/v1/test_noauth.py +++ b/vitrage/tests/functional/api/v1/test_noauth.py @@ -67,11 +67,19 @@ class NoAuthTest(FunctionalTest): with mock.patch('pecan.request') as request: request.client.call.return_value = '{"alarms": []}' params = dict(vitrage_id='all', all_tenants=False) - resp = self.post_json('/alarm/', params=params) + data = self.get_json('/alarm/', params=params) self.assertEqual(1, request.client.call.call_count) - self.assertEqual('200 OK', resp.status) - self.assertEqual([], resp.json) + self.assertEqual([], data) + + def test_noauth_mode_show_alarm(self): + + with mock.patch('pecan.request') as request: + request.client.call.return_value = '{}' + data = self.get_json('/alarm/1234') + + self.assertEqual(1, request.client.call.call_count) + self.assertEqual({}, data) def test_noauth_mode_show_alarm_count(self): with mock.patch('pecan.request') as request: @@ -97,8 +105,7 @@ class NoAuthTest(FunctionalTest): with mock.patch('pecan.request') as request: request.client.call.return_value = '{}' - params = dict(resource_type='all', all_tenants=False) - data = self.get_json('/resources/1234', params=params) + data = self.get_json('/resources/1234') self.assertEqual(1, request.client.call.call_count) self.assertEqual({}, data) diff --git a/vitrage_tempest_tests/tests/api/resources/test_resources.py b/vitrage_tempest_tests/tests/api/resources/test_resources.py index 507629cc0..27b4b1fe3 100644 --- a/vitrage_tempest_tests/tests/api/resources/test_resources.py +++ b/vitrage_tempest_tests/tests/api/resources/test_resources.py @@ -179,7 +179,7 @@ class TestResource(BaseVitrageTempest): sorted_cli_resources = sorted( json.loads(cli_resources), - key=lambda resource: resource["vitrage_id"]) + key=lambda resource: resource["ID"]) sorted_api_resources = sorted( api_resources, key=lambda resource: resource["vitrage_id"]) @@ -190,10 +190,19 @@ class TestResource(BaseVitrageTempest): for cli_resource, api_resource in \ zip(sorted_cli_resources, sorted_api_resources): - for item in self.properties: - self.assertEqual(cli_resource.get(item).lower(), - api_resource.get(item).lower(), - 'for item %s' % item) + + self.assertEqual( + cli_resource.get("ID").lower(), + api_resource.get(VProps.VITRAGE_ID).lower()) + self.assertEqual( + cli_resource.get("Type").lower(), + api_resource.get(VProps.VITRAGE_TYPE).lower()) + self.assertEqual( + cli_resource.get("Data Source ID").lower(), + api_resource.get(VProps.ID).lower()) + self.assertEqual( + cli_resource.get("State").lower(), + api_resource.get(VProps.VITRAGE_OPERATIONAL_STATE).lower()) def _compare_resource_show(self, api_resource_show, cli_resource_show):