Add Tempest tests for invalid IDs and fix potential 500 errors

Add tests that an invalid ID returns 404 for update, patch and delete
methods for alarms, alarm definitions and notification methods.  Some
of these tests already existed but this ensures a complete set of
tests exists for these cases.

Move the resource try catch block wrapper to the correct layer
so that it catches all potential Internal Server Errors and throws
the proper exception.

Change-Id: I07159d0eaed995518bb0c0e2fbf446dff65ec632
This commit is contained in:
Craig Bryant 2017-01-18 18:34:05 -07:00 committed by Andrea Adams
parent 197566f1a7
commit b303044ebd
8 changed files with 105 additions and 30 deletions

View File

@ -53,6 +53,7 @@ class AlarmDefinitions(alarm_definitions_api_v2.AlarmDefinitionsV2API,
LOG.exception(ex)
raise exceptions.RepositoryException(ex)
@resource.resource_try_catch_block
def on_post(self, req, res):
helpers.validate_authorization(req, self._default_authorized_roles)
@ -81,6 +82,7 @@ class AlarmDefinitions(alarm_definitions_api_v2.AlarmDefinitionsV2API,
res.body = helpers.dumpit_utf8(result)
res.status = falcon.HTTP_201
@resource.resource_try_catch_block
def on_get(self, req, res, alarm_definition_id=None):
if alarm_definition_id is None:
helpers.validate_authorization(req, self._get_alarmdefs_authorized_roles)
@ -127,6 +129,7 @@ class AlarmDefinitions(alarm_definitions_api_v2.AlarmDefinitionsV2API,
res.body = helpers.dumpit_utf8(result)
res.status = falcon.HTTP_200
@resource.resource_try_catch_block
def on_put(self, req, res, alarm_definition_id):
helpers.validate_authorization(req, self._default_authorized_roles)
@ -164,6 +167,7 @@ class AlarmDefinitions(alarm_definitions_api_v2.AlarmDefinitionsV2API,
res.body = helpers.dumpit_utf8(result)
res.status = falcon.HTTP_200
@resource.resource_try_catch_block
def on_patch(self, req, res, alarm_definition_id):
helpers.validate_authorization(req, self._default_authorized_roles)
@ -208,6 +212,7 @@ class AlarmDefinitions(alarm_definitions_api_v2.AlarmDefinitionsV2API,
res.body = helpers.dumpit_utf8(result)
res.status = falcon.HTTP_200
@resource.resource_try_catch_block
def on_delete(self, req, res, alarm_definition_id):
helpers.validate_authorization(req, self._default_authorized_roles)
@ -236,7 +241,6 @@ class AlarmDefinitions(alarm_definitions_api_v2.AlarmDefinitionsV2API,
"An alarm definition with the name {} already exists with id {}"
.format(name, found_definition_id))
@resource.resource_try_catch_block
def _alarm_definition_show(self, tenant_id, id):
alarm_definition_row = (
@ -280,7 +284,6 @@ class AlarmDefinitions(alarm_definitions_api_v2.AlarmDefinitionsV2API,
return result
@resource.resource_try_catch_block
def _alarm_definition_delete(self, tenant_id, id):
sub_alarm_definition_rows = (
@ -300,7 +303,6 @@ class AlarmDefinitions(alarm_definitions_api_v2.AlarmDefinitionsV2API,
self._send_alarm_event(u'alarm-deleted', tenant_id, id,
alarm_metric_rows, sub_alarm_rows, None, None)
@resource.resource_try_catch_block
def _alarm_definition_list(self, tenant_id, name, dimensions, severity, req_uri, sort_by,
offset, limit):
@ -358,7 +360,6 @@ class AlarmDefinitions(alarm_definitions_api_v2.AlarmDefinitionsV2API,
LOG.debug(ex)
raise HTTPUnprocessableEntityError('Unprocessable Entity', ex.message)
@resource.resource_try_catch_block
def _alarm_definition_update_or_patch(self, tenant_id,
definition_id,
name,
@ -478,7 +479,6 @@ class AlarmDefinitions(alarm_definitions_api_v2.AlarmDefinitionsV2API,
return sub_alarm_def_update_dict
@resource.resource_try_catch_block
def _alarm_definition_create(self, tenant_id, name, expression,
description, severity, match_by,
alarm_actions, undetermined_actions,

View File

@ -49,6 +49,7 @@ class Alarms(alarms_api_v2.AlarmsV2API,
LOG.exception(ex)
raise exceptions.RepositoryException(ex)
@resource.resource_try_catch_block
def on_put(self, req, res, alarm_id):
helpers.validate_authorization(req, self._default_authorized_roles)
@ -75,6 +76,7 @@ class Alarms(alarms_api_v2.AlarmsV2API,
res.body = helpers.dumpit_utf8(result)
res.status = falcon.HTTP_200
@resource.resource_try_catch_block
def on_patch(self, req, res, alarm_id):
helpers.validate_authorization(req, self._default_authorized_roles)
@ -100,6 +102,7 @@ class Alarms(alarms_api_v2.AlarmsV2API,
res.body = helpers.dumpit_utf8(result)
res.status = falcon.HTTP_200
@resource.resource_try_catch_block
def on_delete(self, req, res, alarm_id):
helpers.validate_authorization(req, self._default_authorized_roles)
@ -108,6 +111,7 @@ class Alarms(alarms_api_v2.AlarmsV2API,
res.status = falcon.HTTP_204
@resource.resource_try_catch_block
def on_get(self, req, res, alarm_id=None):
helpers.validate_authorization(req, self._get_alarms_authorized_roles)
@ -174,7 +178,6 @@ class Alarms(alarms_api_v2.AlarmsV2API,
except Exception as e:
raise HTTPUnprocessableEntityError("Unprocessable Entity", str(e))
@resource.resource_try_catch_block
def _alarm_update(self, tenant_id, alarm_id, new_state, lifecycle_state,
link):
@ -211,7 +214,6 @@ class Alarms(alarms_api_v2.AlarmsV2API,
link, lifecycle_state,
time_ms)
@resource.resource_try_catch_block
def _alarm_patch(self, tenant_id, alarm_id, new_state, lifecycle_state,
link):
@ -248,7 +250,6 @@ class Alarms(alarms_api_v2.AlarmsV2API,
link, lifecycle_state,
time_ms)
@resource.resource_try_catch_block
def _alarm_delete(self, tenant_id, id):
alarm_metric_rows = self._alarms_repo.get_alarm_metrics(id)
@ -263,7 +264,6 @@ class Alarms(alarms_api_v2.AlarmsV2API,
alarm_definition_id, alarm_metric_rows,
sub_alarm_rows, None, None)
@resource.resource_try_catch_block
def _alarm_show(self, req_uri, tenant_id, alarm_id):
alarm_rows = self._alarms_repo.get_alarm(tenant_id, alarm_id)
@ -310,7 +310,6 @@ class Alarms(alarms_api_v2.AlarmsV2API,
return alarm
@resource.resource_try_catch_block
def _alarm_list(self, req_uri, tenant_id, query_parms, offset, limit):
alarm_rows = self._alarms_repo.get_alarms(tenant_id, query_parms,
@ -385,6 +384,7 @@ class AlarmsCount(alarms_api_v2.AlarmsCountV2API, alarming.Alarming):
LOG.exception(ex)
raise exceptions.RepositoryException(ex)
@resource.resource_try_catch_block
def on_get(self, req, res):
helpers.validate_authorization(req, self._get_alarms_authorized_roles)
query_parms = falcon.uri.parse_query_string(req.query_string)
@ -420,7 +420,6 @@ class AlarmsCount(alarms_api_v2.AlarmsCountV2API, alarming.Alarming):
res.body = helpers.dumpit_utf8(result)
res.status = falcon.HTTP_200
@resource.resource_try_catch_block
def _alarms_count(self, req_uri, tenant_id, query_parms, offset, limit):
count_data = self._alarms_repo.get_alarms_count(tenant_id, query_parms, offset, limit)
@ -490,6 +489,7 @@ class AlarmsStateHistory(alarms_api_v2.AlarmsStateHistoryV2API,
LOG.exception(ex)
raise exceptions.RepositoryException(ex)
@resource.resource_try_catch_block
def on_get(self, req, res, alarm_id=None):
if alarm_id is None:
@ -517,7 +517,6 @@ class AlarmsStateHistory(alarms_api_v2.AlarmsStateHistoryV2API,
res.body = helpers.dumpit_utf8(result)
res.status = falcon.HTTP_200
@resource.resource_try_catch_block
def _alarm_history_list(self, tenant_id, start_timestamp,
end_timestamp, query_parms, req_uri, offset,
limit):
@ -542,7 +541,6 @@ class AlarmsStateHistory(alarms_api_v2.AlarmsStateHistoryV2API,
return helpers.paginate(result, req_uri, limit)
@resource.resource_try_catch_block
def _alarm_history(self, tenant_id, alarm_id, req_uri, offset, limit):
result = self._metrics_repo.alarm_history(tenant_id, alarm_id, offset,

View File

@ -1,4 +1,4 @@
# (C) Copyright 2014, 2016 Hewlett Packard Enterprise Development LP
# (C) Copyright 2014-2017 Hewlett Packard Enterprise Development LP
#
# 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
@ -101,7 +101,6 @@ class Metrics(metrics_api_v2.MetricsV2API):
raise falcon.HTTPServiceUnavailable('Service unavailable',
ex.message, 60)
@resource.resource_try_catch_block
def _list_metrics(self, tenant_id, name, dimensions, req_uri, offset,
limit, start_timestamp, end_timestamp):
@ -115,6 +114,7 @@ class Metrics(metrics_api_v2.MetricsV2API):
return helpers.paginate(result, req_uri, limit)
@resource.resource_try_catch_block
def on_post(self, req, res):
helpers.validate_json_content_type(req)
helpers.validate_authorization(req,
@ -129,6 +129,7 @@ class Metrics(metrics_api_v2.MetricsV2API):
self._send_metrics(transformed_metrics)
res.status = falcon.HTTP_204
@resource.resource_try_catch_block
def on_get(self, req, res):
helpers.validate_authorization(req, self._get_metrics_authorized_roles)
tenant_id = (
@ -171,6 +172,7 @@ class MetricsMeasurements(metrics_api_v2.MetricsMeasurementsV2API):
raise falcon.HTTPInternalServerError('Service unavailable',
ex.message)
@resource.resource_try_catch_block
def on_get(self, req, res):
helpers.validate_authorization(req, self._get_metrics_authorized_roles)
tenant_id = (
@ -196,7 +198,6 @@ class MetricsMeasurements(metrics_api_v2.MetricsMeasurementsV2API):
res.body = helpers.dumpit_utf8(result)
res.status = falcon.HTTP_200
@resource.resource_try_catch_block
def _measurement_list(self, tenant_id, name, dimensions, start_timestamp,
end_timestamp, req_uri, offset,
limit, merge_metrics_flag, group_by):
@ -233,6 +234,7 @@ class MetricsStatistics(metrics_api_v2.MetricsStatisticsV2API):
raise falcon.HTTPInternalServerError('Service unavailable',
ex.message)
@resource.resource_try_catch_block
def on_get(self, req, res):
helpers.validate_authorization(req, self._get_metrics_authorized_roles)
tenant_id = (
@ -260,7 +262,6 @@ class MetricsStatistics(metrics_api_v2.MetricsStatisticsV2API):
res.body = helpers.dumpit_utf8(result)
res.status = falcon.HTTP_200
@resource.resource_try_catch_block
def _metric_statistics(self, tenant_id, name, dimensions, start_timestamp,
end_timestamp, statistics, period, req_uri,
offset, limit, merge_metrics_flag, group_by):
@ -298,6 +299,7 @@ class MetricsNames(metrics_api_v2.MetricsNamesV2API):
raise falcon.HTTPInternalServerError('Service unavailable',
ex.message)
@resource.resource_try_catch_block
def on_get(self, req, res):
helpers.validate_authorization(req, self._get_metrics_authorized_roles)
tenant_id = (
@ -311,7 +313,6 @@ class MetricsNames(metrics_api_v2.MetricsNamesV2API):
res.body = helpers.dumpit_utf8(result)
res.status = falcon.HTTP_200
@resource.resource_try_catch_block
def _list_metric_names(self, tenant_id, dimensions, req_uri, offset,
limit):
@ -340,6 +341,7 @@ class DimensionValues(metrics_api_v2.DimensionValuesV2API):
raise falcon.HTTPInternalServerError('Service unavailable',
ex.message)
@resource.resource_try_catch_block
def on_get(self, req, res):
helpers.validate_authorization(req, self._get_metrics_authorized_roles)
tenant_id = (
@ -354,7 +356,6 @@ class DimensionValues(metrics_api_v2.DimensionValuesV2API):
res.body = helpers.dumpit_utf8(result)
res.status = falcon.HTTP_200
@resource.resource_try_catch_block
def _dimension_values(self, tenant_id, req_uri, metric_name,
dimension_name, offset, limit):
@ -384,6 +385,7 @@ class DimensionNames(metrics_api_v2.DimensionNamesV2API):
raise falcon.HTTPInternalServerError('Service unavailable',
ex.message)
@resource.resource_try_catch_block
def on_get(self, req, res):
helpers.validate_authorization(req, self._get_metrics_authorized_roles)
tenant_id = (
@ -396,7 +398,6 @@ class DimensionNames(metrics_api_v2.DimensionNamesV2API):
res.body = helpers.dumpit_utf8(result)
res.status = falcon.HTTP_200
@resource.resource_try_catch_block
def _dimension_names(self, tenant_id, req_uri, metric_name, offset, limit):
result = self._metrics_repo.list_dimension_names(tenant_id,

View File

@ -1,4 +1,4 @@
# (C) Copyright 2014-2016 Hewlett Packard Enterprise Development LP
# (C) Copyright 2014-2017 Hewlett Packard Enterprise Development LP
#
# 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
@ -85,7 +85,6 @@ class Notifications(notifications_api_v2.NotificationsV2API):
.format(nmt))
raise falcon.HTTPBadRequest('Bad Request', "Not a valid notification method type {} ".format(nmt))
@resource.resource_try_catch_block
def _create_notification(self, tenant_id, notification, uri):
name = notification['name']
@ -110,7 +109,6 @@ class Notifications(notifications_api_v2.NotificationsV2API):
period,
uri)
@resource.resource_try_catch_block
def _update_notification(self, notification_id, tenant_id, notification, uri):
name = notification['name']
@ -146,7 +144,6 @@ class Notifications(notifications_api_v2.NotificationsV2API):
return helpers.add_links_to_resource(response, uri)
@resource.resource_try_catch_block
def _list_notifications(self, tenant_id, uri, sort_by, offset, limit):
rows = self._notifications_repo.list_notifications(tenant_id, sort_by,
@ -157,7 +154,6 @@ class Notifications(notifications_api_v2.NotificationsV2API):
return helpers.paginate(result, uri, limit)
@resource.resource_try_catch_block
def _list_notification(self, tenant_id, notification_id, uri):
row = self._notifications_repo.list_notification(
@ -180,13 +176,11 @@ class Notifications(notifications_api_v2.NotificationsV2API):
return result
@resource.resource_try_catch_block
def _delete_notification(self, tenant_id, notification_id):
self._notifications_repo.delete_notification(tenant_id,
notification_id)
@resource.resource_try_catch_block
def _patch_get_notification(self, tenant_id, notification_id, notification):
original_notification = self._notifications_repo.list_notification(tenant_id, notification_id)
if 'name' not in notification:
@ -198,6 +192,7 @@ class Notifications(notifications_api_v2.NotificationsV2API):
if 'period' not in notification:
notification['period'] = original_notification['period']
@resource.resource_try_catch_block
def on_post(self, req, res):
helpers.validate_json_content_type(req)
helpers.validate_authorization(req, self._default_authorized_roles)
@ -207,6 +202,7 @@ class Notifications(notifications_api_v2.NotificationsV2API):
res.body = helpers.dumpit_utf8(result)
res.status = falcon.HTTP_201
@resource.resource_try_catch_block
def on_get(self, req, res, notification_method_id=None):
if notification_method_id is None:
helpers.validate_authorization(req,
@ -243,11 +239,13 @@ class Notifications(notifications_api_v2.NotificationsV2API):
res.body = helpers.dumpit_utf8(result)
res.status = falcon.HTTP_200
@resource.resource_try_catch_block
def on_delete(self, req, res, notification_method_id):
helpers.validate_authorization(req, self._default_authorized_roles)
self._delete_notification(req.project_id, notification_method_id)
res.status = falcon.HTTP_204
@resource.resource_try_catch_block
def on_put(self, req, res, notification_method_id):
helpers.validate_json_content_type(req)
helpers.validate_authorization(req, self._default_authorized_roles)
@ -258,6 +256,7 @@ class Notifications(notifications_api_v2.NotificationsV2API):
res.body = helpers.dumpit_utf8(result)
res.status = falcon.HTTP_200
@resource.resource_try_catch_block
def on_patch(self, req, res, notification_method_id):
helpers.validate_json_content_type(req)
helpers.validate_authorization(req, self._default_authorized_roles)

View File

@ -1,4 +1,4 @@
# (C) Copyright 2016 Hewlett Packard Enterprise Development LP
# (C) Copyright 2016-2017 Hewlett Packard Enterprise Development LP
#
# 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
@ -27,12 +27,12 @@ class NotificationsType(notificationstype_api_v2.NotificationsTypeV2API):
self._notification_method_type_repo = simport.load(
cfg.CONF.repositories.notification_method_type_driver)()
@resource.resource_try_catch_block
def _list_notifications(self, uri, limit):
rows = self._notification_method_type_repo.list_notification_method_types()
result = [dict(type=row) for row in rows]
return helpers.paginate(result, uri, limit)
@resource.resource_try_catch_block
def on_get(self, req, res):
# This is to provide consistency. Pagination is not really supported here as there

View File

@ -911,6 +911,43 @@ class TestAlarmDefinitions(base.BaseMonascaTest):
self.fail("Failed test_create_and_delete_alarm_definition: "
"cannot find the alarm definition just created.")
@test.attr(type="gate")
@test.attr(type=['negative'])
def test_get_alarm_defintion_with_invalid_id(self):
def_id = data_utils.rand_name()
self.assertRaises(exceptions.NotFound,
self.monasca_client.get_alarm_definition, def_id)
@test.attr(type="gate")
@test.attr(type=['negative'])
def test_delete_alarm_defintion_with_invalid_id(self):
def_id = data_utils.rand_name()
self.assertRaises(exceptions.NotFound,
self.monasca_client.delete_alarm_definition, def_id)
@test.attr(type="gate")
@test.attr(type=['negative'])
def test_patch_alarm_defintion_with_invalid_id(self):
def_id = data_utils.rand_name()
self.assertRaises(exceptions.NotFound,
self.monasca_client.patch_alarm_definition,
id=def_id, name='Test')
@test.attr(type="gate")
@test.attr(type=['negative'])
def test_update_alarm_defintion_with_invalid_id(self):
def_id = data_utils.rand_name()
updated_name = data_utils.rand_name('updated_name')
updated_description = 'updated description'
updated_expression = "max(cpu.system_perc) < 0"
self.assertRaises(exceptions.NotFound,
self.monasca_client.update_alarm_definition,
def_id, updated_name, updated_expression,
updated_description, True, ['device'],
'LOW', [], [],
[])
def _create_alarm_definitions(self, expression, number_of_definitions):
self.rule = {'expression': 'mem_total_mb > 0'}
if expression is None:

View File

@ -722,6 +722,27 @@ class TestAlarms(base.BaseMonascaTest):
self.assertRaises(exceptions.NotFound,
self.monasca_client.delete_alarm, id)
@test.attr(type="gate")
@test.attr(type=['negative'])
def test_patch_alarm_with_invalid_id(self):
id = data_utils.rand_name()
self.assertRaises(exceptions.NotFound,
self.monasca_client.patch_alarm, id=id,
lifecycle_state="OPEN")
@test.attr(type="gate")
@test.attr(type=['negative'])
def test_update_alarm_with_invalid_id(self):
alarm_id = data_utils.rand_name()
updated_state = "ALARM"
updated_lifecycle_state = "OPEN"
updated_link = "http://somesite.com"
self.assertRaises(exceptions.NotFound,
self.monasca_client.update_alarm,
id=alarm_id, state=updated_state,
lifecycle_state=updated_lifecycle_state,
link=updated_link)
@test.attr(type="gate")
def test_create_alarms_with_match_by(self):
# Create an alarm definition with no match_by

View File

@ -1,4 +1,4 @@
# (C) Copyright 2015-2016 Hewlett Packard Enterprise Development LP
# (C) Copyright 2015-2017 Hewlett Packard Enterprise Development LP
#
# 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
@ -733,6 +733,25 @@ class TestNotificationMethods(base.BaseMonascaTest):
delete_notification_method(response_body['id'])
self.assertEqual(204, resp.status)
@test.attr(type="gate")
@test.attr(type=['negative'])
def test_patch_notification_method_with_invalid_id(self):
id = data_utils.rand_name()
name = data_utils.rand_name('notification-')
self.assertRaises(exceptions.NotFound,
self.monasca_client.patch_notification_method,
id, name)
@test.attr(type="gate")
@test.attr(type=['negative'])
def test_update_notification_method_with_invalid_id(self):
id = data_utils.rand_name()
name = data_utils.rand_name('notification-')
self.assertRaises(exceptions.NotFound,
self.monasca_client.update_notification_method, id,
name=name, type='EMAIL',
address='bob@thebridge.org', period=0)
@test.attr(type="gate")
@test.attr(type=['negative'])
def test_update_email_notification_method_with_nonzero_period(self):