Add functional integration test coverage for Aodh
These additional tests are covering the former fuctional
test from Aodh in-tree tests.
Change-Id: Ib6df8cd9d6c09736df1b13a8bb4b0781ef1dafa3
diff --git a/telemetry_tempest_plugin/aodh/api/test_alarming_api.py b/telemetry_tempest_plugin/aodh/api/test_alarming_api.py
index 9032a1a..fbdaa75 100644
--- a/telemetry_tempest_plugin/aodh/api/test_alarming_api.py
+++ b/telemetry_tempest_plugin/aodh/api/test_alarming_api.py
@@ -9,6 +9,8 @@
# 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 random
+
from tempest import config
from tempest.lib.common.utils import data_utils
from tempest.lib import decorators
@@ -61,6 +63,15 @@
alarm_id = body['alarm_id']
self.assertDictContainsSubset(self.rule, body['event_rule'])
+ # Update alarm with same rule and name
+ body = self.alarming_client.update_alarm(
+ alarm_id,
+ event_rule=self.rule,
+ name=alarm_name,
+ type='event')
+ self.assertEqual(alarm_name, body['name'])
+ self.assertDictContainsSubset(self.rule, body['event_rule'])
+
# Update alarm with new rule and new name
new_rule = {
"event_type": "compute.instance.create",
@@ -79,10 +90,19 @@
self.assertEqual(alarm_name_updated, body['name'])
self.assertDictContainsSubset(new_rule, body['event_rule'])
+ # Update severity
+ body = self.alarming_client.update_alarm(
+ alarm_id,
+ event_rule=new_rule,
+ name=alarm_name_updated,
+ type='event',
+ severity='low')
+
# Get and verify details of an alarm after update
body = self.alarming_client.show_alarm(alarm_id)
self.assertEqual(alarm_name_updated, body['name'])
self.assertDictContainsSubset(new_rule, body['event_rule'])
+ self.assertEqual('low', body['severity'])
# Get history for the alarm and verify the same
body = self.alarming_client.show_alarm_history(alarm_id)
@@ -91,11 +111,141 @@
self.assertEqual("creation", body[1]['type'])
self.assertIn(alarm_name, body[1]['detail'])
+ # Query by state and verify
+ query = ['state', 'eq', 'insufficient data']
+ body = self.alarming_client.list_alarms(query)
+ self.assertNotEqual(0, len(body))
+ self.assertEqual(set(['insufficient data']),
+ set(alarm['state'] for alarm in body))
+
+ # Query by type and verify
+ query = ['type', 'eq', 'event']
+ body = self.alarming_client.list_alarms(query)
+ self.assertNotEqual(0, len(body))
+ self.assertEqual(set(['event']), set(alarm['type'] for alarm in body))
+
# Delete alarm and verify if deleted
self.alarming_client.delete_alarm(alarm_id)
self.assertRaises(lib_exc.NotFound,
self.alarming_client.show_alarm, alarm_id)
+ @decorators.idempotent_id('b666f7d5-ce8c-4b2d-887e-15993433c2e9')
+ def test_create_query_delete_disabled_alarm(self):
+ # Create an alarm
+ alarm_name = data_utils.rand_name('telemetry_alarm')
+ body = self.alarming_client.create_alarm(
+ name=alarm_name, type='event', enabled=False,
+ event_rule=self.rule)
+ self.assertEqual(alarm_name, body['name'])
+ alarm_id = body['alarm_id']
+ self.assertDictContainsSubset(self.rule, body['event_rule'])
+ self.assertFalse(body['enabled'])
+
+ # Query by enabled false and verify
+ query = ['enabled', 'eq', 'false']
+ body = self.alarming_client.list_alarms(query)
+ self.assertNotEqual(0, len(body))
+ self.assertEqual(set([False]),
+ set(alarm['enabled'] for alarm in body))
+
+ # Delete alarm and verify if deleted
+ self.alarming_client.delete_alarm(alarm_id)
+ self.assertRaises(lib_exc.NotFound,
+ self.alarming_client.show_alarm, alarm_id)
+
+ @decorators.idempotent_id('bf44b72f-0384-4b34-ab78-bdfd3bf1b16c')
+ def test_create_delete_alarm_defaults(self):
+ # Create an alarm
+ alarm_name = data_utils.rand_name('telemetry_alarm')
+ body = self.alarming_client.create_alarm(
+ name=alarm_name, type='event',
+ event_rule=self.rule)
+ self.assertEqual(alarm_name, body['name'])
+ alarm_id = body['alarm_id']
+ self.assertDictContainsSubset(self.rule, body['event_rule'])
+
+ # Verify default
+ expected_defaults = {
+ 'enabled': True,
+ 'ok_actions': [],
+ 'alarm_actions': [],
+ 'insufficient_data_actions': [],
+ 'repeat_actions': False,
+ }
+ for key in expected_defaults:
+ self.assertEqual(expected_defaults[key], body[key])
+
+ # Delete alarm and verify if deleted
+ self.alarming_client.delete_alarm(alarm_id)
+ self.assertRaises(lib_exc.NotFound,
+ self.alarming_client.show_alarm, alarm_id)
+
+ @decorators.idempotent_id('0a883365-7158-4dbb-a946-3b07dc171c93')
+ def test_create_excercise_delete_alarm_state(self):
+ # Create an alarm
+ alarm_name = data_utils.rand_name('telemetry_alarm')
+ body = self.alarming_client.create_alarm(
+ name=alarm_name, type='event',
+ event_rule=self.rule)
+ self.assertEqual(alarm_name, body['name'])
+ alarm_id = body['alarm_id']
+ self.assertDictContainsSubset(self.rule, body['event_rule'])
+
+ # Verify initial state
+ body = self.alarming_client.show_alarm(alarm_id)
+ self.assertEqual("insufficient data", body['state'])
+ self.assertEqual("Not evaluated yet", body['state_reason'])
+
+ # Update state and verify
+ self.alarming_client.alarm_set_state(alarm_id, state='ok')
+ body = self.alarming_client.show_alarm(alarm_id)
+ self.assertEqual('ok', body['state'])
+ self.assertEqual('Manually set via API', body['state_reason'])
+
+ # Update state and verify reason read only
+ body['state'] = 'alarm'
+ body['state_reason'] = 'Oops!'
+ self.alarming_client.update_alarm(**body)
+ body = self.alarming_client.show_alarm(alarm_id)
+ self.assertEqual('alarm', body['state'])
+ self.assertEqual('Manually set via API', body['state_reason'])
+
+ # Delete alarm and verify if deleted
+ self.alarming_client.delete_alarm(alarm_id)
+ self.assertRaises(lib_exc.NotFound,
+ self.alarming_client.show_alarm, alarm_id)
+
+ @decorators.idempotent_id('ced16dd6-cbd8-4a2b-a9c3-aed6e4a7102c')
+ def test_create_query_delete_alarm_same_name(self):
+ # Create two alarms with same name
+ alarm_name = data_utils.rand_name('telemetry_alarm')
+ body1 = self.alarming_client.create_alarm(
+ name=alarm_name, type='event',
+ event_rule=self.rule)
+ body2 = self.alarming_client.create_alarm(
+ name=alarm_name, type='event',
+ event_rule=self.rule)
+ alarm1_id = body1['alarm_id']
+ alarm2_id = body2['alarm_id']
+ self.assertEqual(alarm_name, body1['name'])
+ self.assertEqual(alarm_name, body2['name'])
+ self.assertNotEqual(alarm1_id, alarm2_id)
+
+ # Query by name and verify
+ query = ['name', 'eq', alarm_name]
+ body = self.alarming_client.list_alarms(query)
+ self.assertEqual(2, len(body))
+ self.assertEqual(set([alarm_name]),
+ set(alarm['name'] for alarm in body))
+
+ # Delete alarms and verify if deleted
+ self.alarming_client.delete_alarm(alarm1_id)
+ self.assertRaises(lib_exc.NotFound,
+ self.alarming_client.show_alarm, alarm1_id)
+ self.alarming_client.delete_alarm(alarm2_id)
+ self.assertRaises(lib_exc.NotFound,
+ self.alarming_client.show_alarm, alarm2_id)
+
@decorators.idempotent_id('aca49486-70bb-4016-87e0-f6131374f742')
def test_set_get_alarm_state(self):
alarm_states = ['ok', 'alarm', 'insufficient data']
@@ -111,3 +261,141 @@
# Get alarm state and verify
state = self.alarming_client.show_alarm_state(alarm['alarm_id'])
self.assertEqual(new_state, state.data)
+
+ @decorators.idempotent_id('0cc2f5d1-6f48-4274-bfa8-f62f82eab6ed')
+ def test_get_capabilities(self):
+
+ response = self.alarming_client.show_capabilities()
+ self.assertIsNotNone(response)
+ self.assertNotEqual({}, response)
+ self.assertIn('api', response)
+ self.assertIn('alarm_storage', response)
+
+ @decorators.idempotent_id('d42d0103-0497-4109-9746-dacaa17e831c')
+ def test_get_versions(self):
+
+ response = self.alarming_client.show_version()
+ media_types = [
+ {
+ 'base': 'application/json',
+ 'type': 'application/vnd.openstack.telemetry-v2+json'
+ }, {
+ 'base': 'application/xml',
+ 'type': 'application/vnd.openstack.telemetry-v2+xml'
+ }
+ ]
+ self.assertIsNotNone(response)
+ self.assertNotEqual({}, response)
+ self.assertEqual('v2', response['versions']['values'][0]['id'])
+ self.assertIn('links', response['versions']['values'][0])
+ self.assertEqual(media_types, response['versions']['values'][0][
+ 'media-types'])
+ self.assertIn('status', response['versions']['values'][0])
+ self.assertIn('updated', response['versions']['values'][0])
+
+ @decorators.idempotent_id('a45a95f6-7855-4dbc-a507-af0f48cc3370')
+ def test_create_n_delete_alarm_duplicate_actions(self):
+ # create dual actions
+ alarm_name = data_utils.rand_name('telemetry_alarm')
+ rule = {'metrics': ['41869681-5776-46d6-91ed-cccc43b6e4e3',
+ 'a1fb80f4-c242-4f57-87c6-68f47521059e'],
+ 'aggregation_method': 'mean',
+ 'comparison_operator': 'eq',
+ 'threshold': 300.0}
+ body = self.alarming_client.create_alarm(
+ name=alarm_name,
+ alarm_actions=['http://no.where', 'http://no.where'],
+ type='gnocchi_aggregation_by_metrics_threshold',
+ gnocchi_aggregation_by_metrics_threshold_rule=rule
+ )
+ alarm_id = body['alarm_id']
+ alarms = self.alarming_client.list_alarms(['name', 'eq', alarm_name])
+ self.assertEqual(1, len(alarms))
+ self.assertEqual(['http://no.where'], alarms[0]['alarm_actions'])
+
+ # Delete alarm and verify if deleted
+ self.alarming_client.delete_alarm(alarm_id)
+ self.assertRaises(lib_exc.NotFound,
+ self.alarming_client.show_alarm, alarm_id)
+
+ @decorators.idempotent_id('c1dcefdf-3b96-40d0-8f39-04fc0702ab6b')
+ def test_create_n_delete_alarm_rule_loadbalancer(self):
+ # create dual actions
+ alarm_name = data_utils.rand_name('telemetry_alarm')
+ rule = {
+ "pool_id": "2177ccd8-b09c-417a-89a0-e8d2419be612",
+ "stack_id": "1b974012-ebcb-4888-8ae2-47714d4d2c4d",
+ "autoscaling_group_id": "681c9266-61d2-4c9a-ad18-526807f6adc0"
+ }
+ body = self.alarming_client.create_alarm(
+ name=alarm_name,
+ type='loadbalancer_member_health',
+ loadbalancer_member_health_rule=rule
+ )
+ alarm_id = body['alarm_id']
+ alarms = self.alarming_client.list_alarms(['name', 'eq', alarm_name])
+ self.assertEqual(1, len(alarms))
+
+ # Check the actions are empty
+ self.assertEqual([], alarms[0]['alarm_actions'])
+
+ # Delete alarm and verify if deleted
+ self.alarming_client.delete_alarm(alarm_id)
+ self.assertRaises(lib_exc.NotFound,
+ self.alarming_client.show_alarm, alarm_id)
+
+ @decorators.idempotent_id('e1d65c3c-a64d-4968-949c-96f2b2d8b363')
+ def test_create_list_sort_limit_delete_alarm(self):
+ # create test alarms
+ alarms = {}
+ for i in range(3):
+ alarm_name = data_utils.rand_name('sorted_alarms')
+ alarms[alarm_name] = []
+ for j in range(random.randint(2, 4)):
+ body = self.alarming_client.create_alarm(
+ name=alarm_name, type='event',
+ event_rule=self.rule)
+ alarms[alarm_name].append(body['alarm_id'])
+ ordered_alarms = []
+ for key in sorted(alarms):
+ ordered_alarms.extend([(key, a) for a in sorted(alarms[key])])
+
+ # Sort by name and verify
+ sort = ['name:asc']
+ body = self.alarming_client.list_alarms(sort=sort)
+ self.assertEqual([alarm[0] for alarm in ordered_alarms],
+ [alarm['name'] for alarm in body if alarm[
+ 'name'].startswith('tempest-sorted_alarms')])
+ sort = ['name']
+ body = self.alarming_client.list_alarms(sort=sort)
+ self.assertEqual([alarm[0] for alarm in ordered_alarms],
+ [alarm['name'] for alarm in body if alarm[
+ 'name'].startswith('tempest-sorted_alarms')])
+
+ # multiple sorts
+ sort = ['name:asc', 'alarm_id:asc']
+ body = self.alarming_client.list_alarms(sort=sort)
+ name_ids = [(a['name'], a['alarm_id']) for a in body if a[
+ 'name'].startswith('tempest-sorted_alarms')]
+ self.assertEqual(ordered_alarms, name_ids)
+
+ # limit and sort
+ sort = ['name:asc', 'alarm_id:asc']
+ limit = 2
+ body = self.alarming_client.list_alarms(limit=limit)
+ self.assertEqual(2, len(body))
+ body = self.alarming_client.list_alarms(sort=sort, limit=limit)
+ self.assertEqual(2, len(body))
+ self.assertEqual([ordered_alarms[0][0], ordered_alarms[1][0]],
+ [body[0]['name'], body[1]['name']])
+ body = self.alarming_client.list_alarms(
+ sort=sort, marker=ordered_alarms[1][1])
+ name_ids = [(a['name'], a['alarm_id']) for a in body if a[
+ 'name'].startswith('tempest-sorted_alarms')]
+ self.assertEqual(ordered_alarms[2:], name_ids)
+
+ # Delete alarms and verify if deleted
+ for name, alarm_id in ordered_alarms:
+ self.alarming_client.delete_alarm(alarm_id)
+ self.assertRaises(lib_exc.NotFound,
+ self.alarming_client.show_alarm, alarm_id)
diff --git a/telemetry_tempest_plugin/aodh/api/test_alarming_api_negative.py b/telemetry_tempest_plugin/aodh/api/test_alarming_api_negative.py
index bc5650f..a1bc0d8 100644
--- a/telemetry_tempest_plugin/aodh/api/test_alarming_api_negative.py
+++ b/telemetry_tempest_plugin/aodh/api/test_alarming_api_negative.py
@@ -12,6 +12,8 @@
# License for the specific language governing permissions and limitations
# under the License.
+import datetime
+
from oslo_utils import uuidutils
from tempest.lib.common.utils import data_utils
from tempest.lib import decorators
@@ -38,6 +40,32 @@
non_existent_id)
@decorators.attr(type=['negative'])
+ @decorators.idempotent_id('d6adbf73-03ca-4730-b1d7-13233475d271')
+ def test_get_non_existent_alarm_timestamp(self):
+ # list invalid query
+ date_time = datetime.datetime(2012, 7, 2, 10, 41)
+ isotime = date_time.isoformat()
+ query = ['timestamp', 'gt', isotime]
+ self.assertRaises(lib_exc.BadRequest, self.alarming_client.list_alarms,
+ query)
+
+ @decorators.attr(type=['negative'])
+ @decorators.idempotent_id('f6f9e0a3-9623-43bb-a39a-284073c38b1f')
+ def test_get_alarms_all_projects(self):
+ # don't get alarms from all projects
+ query = ['all_projects', 'eq', 'true']
+ self.assertRaises(lib_exc.Forbidden, self.alarming_client.list_alarms,
+ query)
+
+ @decorators.attr(type=['negative'])
+ @decorators.idempotent_id('b3f61233-92f6-4402-a98d-fd5858379949')
+ def test_get_alarms_invalid_op(self):
+ # fail on op
+ query = ['project_id', 'ne', 'b3f61233-92f6-4402-a98d-fd5858379949']
+ self.assertRaises(lib_exc.BadRequest, self.alarming_client.list_alarms,
+ query)
+
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('ef45000d-0a72-4781-866d-4cb7bf2582ae')
def test_get_update_show_history_delete_deleted_alarm(self):
# get, update and delete the deleted alarm
@@ -73,3 +101,480 @@
# delete the deleted alarm
self.assertRaises(lib_exc.NotFound, self.alarming_client.delete_alarm,
alarm_id)
+
+ @decorators.attr(type=['negative'])
+ @decorators.idempotent_id('3bd874d7-dcbf-410e-ab08-829969282d92')
+ def test_create_invalid_alarm_constraint_start(self):
+ # create bad time constraint
+ alarm_name = data_utils.rand_name('telemetry_alarm')
+ tc_name = data_utils.rand_name('time_constraint')
+ rule = {'metrics': ['41869681-5776-46d6-91ed-cccc43b6e4e3',
+ 'a1fb80f4-c242-4f57-87c6-68f47521059e'],
+ 'aggregation_method': 'mean',
+ 'comparison_operator': 'eq',
+ 'threshold': 300.0}
+ tc = [{'name': tc_name,
+ 'start': '11:00am',
+ 'duration': 10}]
+ self.assertRaises(
+ lib_exc.BadRequest,
+ self.alarming_client.create_alarm,
+ name=alarm_name,
+ type='gnocchi_aggregation_by_metrics_threshold',
+ time_constraints=tc,
+ gnocchi_aggregation_by_metrics_threshold_rule=rule)
+
+ @decorators.skip_because(bug="2045115")
+ @decorators.attr(type=['negative'])
+ @decorators.idempotent_id('384dec5a-af5b-4a9e-8dc1-9d056fca71d6')
+ def test_create_null_alarm_constraint(self):
+ # create bad time constraint
+ alarm_name = data_utils.rand_name('telemetry_alarm')
+ rule = {'metrics': ['41869681-5776-46d6-91ed-cccc43b6e4e3',
+ 'a1fb80f4-c242-4f57-87c6-68f47521059e'],
+ 'aggregation_method': 'mean',
+ 'comparison_operator': 'eq',
+ 'threshold': 300.0}
+ tc = None
+ self.assertRaises(
+ lib_exc.BadRequest,
+ self.alarming_client.create_alarm,
+ name=alarm_name,
+ type='gnocchi_aggregation_by_metrics_threshold',
+ time_constraints=tc,
+ gnocchi_aggregation_by_metrics_threshold_rule=rule)
+
+ @decorators.attr(type=['negative'])
+ @decorators.idempotent_id('f358ee93-798e-4641-8fdc-13dc2f7a71c5')
+ def test_create_duplicate_alarm_constraint_name(self):
+ # create bad time constraint
+ alarm_name = data_utils.rand_name('telemetry_alarm')
+ tc_name = data_utils.rand_name('time_constraint')
+ rule = {'metrics': ['41869681-5776-46d6-91ed-cccc43b6e4e3',
+ 'a1fb80f4-c242-4f57-87c6-68f47521059e'],
+ 'aggregation_method': 'mean',
+ 'comparison_operator': 'eq',
+ 'threshold': 300.0}
+ tc = [{'name': tc_name,
+ 'start': '* 11 * * *',
+ 'duration': 10},
+ {'name': tc_name,
+ 'start': '* * * * *',
+ 'duration': 20}]
+ self.assertRaises(
+ lib_exc.BadRequest,
+ self.alarming_client.create_alarm,
+ name=alarm_name,
+ type='gnocchi_aggregation_by_metrics_threshold',
+ time_constraints=tc,
+ gnocchi_aggregation_by_metrics_threshold_rule=rule)
+
+ @decorators.attr(type=['negative'])
+ @decorators.idempotent_id('e1d5d605-61a0-415b-bcaf-dc3a15717344')
+ def test_create_invalid_alarm_constraint_timezone(self):
+ # create bad time constraint
+ alarm_name = data_utils.rand_name('telemetry_alarm')
+ tc_name = data_utils.rand_name('time_constraint')
+ rule = {'metrics': ['41869681-5776-46d6-91ed-cccc43b6e4e3',
+ 'a1fb80f4-c242-4f57-87c6-68f47521059e'],
+ 'aggregation_method': 'mean',
+ 'comparison_operator': 'eq',
+ 'threshold': 300.0}
+ tc = [{'name': tc_name,
+ 'start': '* 11 * * *',
+ 'duration': 10,
+ 'timezone': 'aaaa'}]
+ self.assertRaises(
+ lib_exc.BadRequest,
+ self.alarming_client.create_alarm,
+ name=alarm_name,
+ type='gnocchi_aggregation_by_metrics_threshold',
+ time_constraints=tc,
+ gnocchi_aggregation_by_metrics_threshold_rule=rule)
+
+ @decorators.attr(type=['negative'])
+ @decorators.idempotent_id('7d3ee20c-8a0f-4a43-8a1a-2168da9905da')
+ def test_create_invalid_alarm_constraint_duration(self):
+ # create bad time constraint
+ alarm_name = data_utils.rand_name('telemetry_alarm')
+ tc_name = data_utils.rand_name('time_constraint')
+ rule = {'metrics': ['41869681-5776-46d6-91ed-cccc43b6e4e3',
+ 'a1fb80f4-c242-4f57-87c6-68f47521059e'],
+ 'aggregation_method': 'mean',
+ 'comparison_operator': 'eq',
+ 'threshold': 300.0}
+ tc = [{'name': tc_name,
+ 'start': '* 11 * * *',
+ 'duration': -10}]
+ self.assertRaises(
+ lib_exc.BadRequest,
+ self.alarming_client.create_alarm,
+ name=alarm_name,
+ type='gnocchi_aggregation_by_metrics_threshold',
+ time_constraints=tc,
+ gnocchi_aggregation_by_metrics_threshold_rule=rule)
+
+ @decorators.attr(type=['negative'])
+ @decorators.idempotent_id('21ab2919-8680-44d8-be90-57aac5b22474')
+ def test_create_invalid_alarm_rule_granularity(self):
+ # create bad time constraint
+ alarm_name = data_utils.rand_name('telemetry_alarm')
+ tc_name = data_utils.rand_name('time_constraint')
+ rule = {'metrics': ['41869681-5776-46d6-91ed-cccc43b6e4e3',
+ 'a1fb80f4-c242-4f57-87c6-68f47521059e'],
+ 'aggregation_method': 'mean',
+ 'comparison_operator': 'eq',
+ 'threshold': 300.0,
+ 'granularity': -1}
+ tc = [{'name': tc_name,
+ 'start': '* 11 * * *',
+ 'duration': 10}]
+ self.assertRaises(
+ lib_exc.BadRequest,
+ self.alarming_client.create_alarm,
+ name=alarm_name,
+ type='gnocchi_aggregation_by_metrics_threshold',
+ time_constraints=tc,
+ gnocchi_aggregation_by_metrics_threshold_rule=rule)
+
+ @decorators.attr(type=['negative'])
+ @decorators.idempotent_id('b942eeb3-7fc2-4ba4-ad8f-674f3ac7fb96')
+ def test_create_invalid_alarm_none_rule(self):
+ # create bad time constraint
+ alarm_name = data_utils.rand_name('telemetry_alarm')
+ tc_name = data_utils.rand_name('time_constraint')
+ rule = None
+ tc = [{'name': tc_name,
+ 'start': '* 11 * * *',
+ 'duration': -10}]
+ self.assertRaises(
+ lib_exc.BadRequest,
+ self.alarming_client.create_alarm,
+ name=alarm_name,
+ type='gnocchi_aggregation_by_metrics_threshold',
+ time_constraints=tc,
+ gnocchi_aggregation_by_metrics_threshold_rule=rule)
+
+ @decorators.attr(type=['negative'])
+ @decorators.idempotent_id('a805f2f3-ea3e-4574-b616-3dbe86cb95f8')
+ def test_create_invalid_alarm_input_state(self):
+ # create bad time constraint
+ alarm_name = data_utils.rand_name('telemetry_alarm')
+ rule = {'metrics': ['41869681-5776-46d6-91ed-cccc43b6e4e3',
+ 'a1fb80f4-c242-4f57-87c6-68f47521059e'],
+ 'aggregation_method': 'mean',
+ 'comparison_operator': 'eq',
+ 'threshold': 300.0}
+ self.assertRaises(
+ lib_exc.BadRequest,
+ self.alarming_client.create_alarm,
+ name=alarm_name,
+ state='bad_state',
+ type='gnocchi_aggregation_by_metrics_threshold',
+ gnocchi_aggregation_by_metrics_threshold_rule=rule)
+
+ @decorators.attr(type=['negative'])
+ @decorators.idempotent_id('ea90ef4a-135d-48f2-b984-c156a89f627c')
+ def test_create_invalid_alarm_input_severity(self):
+ # create bad time constraint
+ alarm_name = data_utils.rand_name('telemetry_alarm')
+ rule = {'metrics': ['41869681-5776-46d6-91ed-cccc43b6e4e3',
+ 'a1fb80f4-c242-4f57-87c6-68f47521059e'],
+ 'aggregation_method': 'mean',
+ 'comparison_operator': 'eq',
+ 'threshold': 300.0}
+ self.assertRaises(
+ lib_exc.BadRequest,
+ self.alarming_client.create_alarm,
+ name=alarm_name,
+ state='ok',
+ severity='bad_value',
+ type='gnocchi_aggregation_by_metrics_threshold',
+ gnocchi_aggregation_by_metrics_threshold_rule=rule)
+
+ @decorators.attr(type=['negative'])
+ @decorators.idempotent_id('7373e681-8e11-4071-a955-aa9582bef914')
+ def test_create_invalid_alarm_input_type(self):
+ # create bad time constraint
+ alarm_name = data_utils.rand_name('telemetry_alarm')
+ rule = {'metrics': ['41869681-5776-46d6-91ed-cccc43b6e4e3',
+ 'a1fb80f4-c242-4f57-87c6-68f47521059e'],
+ 'aggregation_method': 'mean',
+ 'comparison_operator': 'eq',
+ 'threshold': 300.0}
+ self.assertRaises(
+ lib_exc.BadRequest,
+ self.alarming_client.create_alarm,
+ name=alarm_name,
+ state='ok',
+ type='bad_type',
+ bad_type_rule=rule)
+
+ @decorators.attr(type=['negative'])
+ @decorators.idempotent_id('3af990af-f134-4b87-9234-a25280afc176')
+ def test_create_invalid_alarm_input_enabled_string(self):
+ # create bad time constraint
+ alarm_name = data_utils.rand_name('telemetry_alarm')
+ rule = {'metrics': ['41869681-5776-46d6-91ed-cccc43b6e4e3',
+ 'a1fb80f4-c242-4f57-87c6-68f47521059e'],
+ 'aggregation_method': 'mean',
+ 'comparison_operator': 'eq',
+ 'threshold': 300.0}
+ self.assertRaises(
+ lib_exc.BadRequest,
+ self.alarming_client.create_alarm,
+ name=alarm_name,
+ state='ok',
+ enabled='bad_value',
+ type='gnocchi_aggregation_by_metrics_threshold',
+ gnocchi_aggregation_by_metrics_threshold_rule=rule)
+
+ @decorators.skip_because(bug="2045118")
+ @decorators.attr(type=['negative'])
+ @decorators.idempotent_id('db999b7e-3bc8-4be3-90f3-9d0034f61e8a')
+ def test_create_invalid_alarm_input_enabled_int(self):
+ # create bad time constraint
+ alarm_name = data_utils.rand_name('telemetry_alarm')
+ rule = {'metrics': ['41869681-5776-46d6-91ed-cccc43b6e4e3',
+ 'a1fb80f4-c242-4f57-87c6-68f47521059e'],
+ 'aggregation_method': 'mean',
+ 'comparison_operator': 'eq',
+ 'threshold': 300.0}
+ self.assertRaises(
+ lib_exc.BadRequest,
+ self.alarming_client.create_alarm,
+ name=alarm_name,
+ state='ok',
+ enabled=0,
+ type='gnocchi_aggregation_by_metrics_threshold',
+ gnocchi_aggregation_by_metrics_threshold_rule=rule)
+
+ @decorators.attr(type=['negative'])
+ @decorators.idempotent_id('0c64f10f-8529-46a5-9172-f0a64789632f')
+ def test_create_invalid_alarm_statistic(self):
+ # create bad time constraint
+ alarm_name = data_utils.rand_name('telemetry_alarm')
+ rule = {'metrics': ['41869681-5776-46d6-91ed-cccc43b6e4e3',
+ 'a1fb80f4-c242-4f57-87c6-68f47521059e'],
+ 'aggregation_method': 'magic',
+ 'comparison_operator': 'gt',
+ 'threshold': 2.0}
+ self.assertRaises(
+ lib_exc.BadRequest,
+ self.alarming_client.create_alarm,
+ name=alarm_name,
+ type='gnocchi_aggregation_by_metrics_threshold',
+ gnocchi_aggregation_by_metrics_threshold_rule=rule)
+
+ @decorators.attr(type=['negative'])
+ @decorators.idempotent_id('0c421fe7-69e9-4a7c-9df0-547f7bc4c91a')
+ def test_create_invalid_alarm_com_op(self):
+ # create bad time constraint
+ alarm_name = data_utils.rand_name('telemetry_alarm')
+ rule = {'metrics': ['41869681-5776-46d6-91ed-cccc43b6e4e3',
+ 'a1fb80f4-c242-4f57-87c6-68f47521059e'],
+ 'comparison_operator': 'bd_co',
+ 'threshold': 20.0}
+ self.assertRaises(
+ lib_exc.BadRequest,
+ self.alarming_client.create_alarm,
+ name=alarm_name,
+ type='gnocchi_aggregation_by_metrics_threshold',
+ gnocchi_aggregation_by_metrics_threshold_rule=rule)
+
+ @decorators.attr(type=['negative'])
+ @decorators.idempotent_id('6d3f39dd-64ee-438d-804e-f193dc2c7d4b')
+ def test_list_invalid_project(self):
+ # Get invalid paths
+ self.assertRaises(
+ lib_exc.Unauthorized,
+ self.alarming_client.list_alarms,
+ ['project_id', 'eq', 'other-project'])
+
+ @decorators.attr(type=['negative'])
+ @decorators.idempotent_id('c4718d72-3091-4965-b3f2-6d6c2375d26a')
+ def test_list_non_existing_history(self):
+ # Get invalid paths
+ alarm_id = data_utils.rand_name('aid')
+ self.assertRaises(
+ lib_exc.NotFound,
+ self.alarming_client.show_alarm_history,
+ alarm_id=alarm_id)
+
+ @decorators.attr(type=['negative'])
+ @decorators.idempotent_id('5bbccedd-dd80-462c-a6da-d796d7667b5e')
+ def test_create_alarm_wsme_workaround(self):
+ # create bad time constraint
+ alarm_name = data_utils.rand_name('telemetry_alarm')
+ rule_key = 'gnocchi_aggregation_by_metrics_threshold_rule'
+ rules = {
+ 'type': {
+ 'name': alarm_name,
+ rule_key: {
+ 'metrics': ['41869681-5776-46d6-91ed-cccc43b6e4e3',
+ 'a1fb80f4-c242-4f57-87c6-68f47521059e'],
+ 'aggregation_method': 'mean',
+ 'threshold': 2.0,
+ }
+ },
+ 'name': {
+ 'type': 'gnocchi_aggregation_by_metrics_threshold',
+ rule_key: {
+ 'metrics': ['41869681-5776-46d6-91ed-cccc43b6e4e3',
+ 'a1fb80f4-c242-4f57-87c6-68f47521059e'],
+ 'aggregation_method': 'mean',
+ 'threshold': 2.0,
+ }
+ },
+ 'threshold_rule/metrics': {
+ 'name': alarm_name,
+ 'type': 'gnocchi_aggregation_by_metrics_threshold',
+ rule_key: {
+ 'aggregation_method': 'mean',
+ 'threshold': 2.0,
+ }
+ },
+ 'threshold_rule/threshold': {
+ 'name': alarm_name,
+ 'type': 'gnocchi_aggregation_by_metrics_threshold',
+ rule_key: {
+ 'metrics': ['41869681-5776-46d6-91ed-cccc43b6e4e3',
+ 'a1fb80f4-c242-4f57-87c6-68f47521059e'],
+ 'aggregation_method': 'mean',
+ }
+ },
+ }
+
+ for field, adef in rules.items():
+ self.assertRaises(
+ lib_exc.BadRequest,
+ self.alarming_client.create_alarm,
+ name=getattr(adef, 'name', None),
+ type=getattr(adef, 'type', None),
+ gnocchi_aggregation_by_metrics_threshold_rule=adef[rule_key]
+ )
+
+ def _do_create_alarm_invalid_action(self, ok_actions=None,
+ alarm_actions=None,
+ insufficient_data_actions=None,
+ error_message=None):
+
+ ok_actions = ok_actions or []
+ alarm_actions = alarm_actions or []
+ insufficient_data_actions = insufficient_data_actions or []
+ rule_key = 'gnocchi_aggregation_by_metrics_threshold_rule'
+ alarm_name = data_utils.rand_name('telemetry_alarm')
+ adef = {
+ 'enabled': False,
+ 'state': 'ok',
+ 'type': 'gnocchi_aggregation_by_metrics_threshold',
+ 'ok_actions': ok_actions,
+ 'alarm_actions': alarm_actions,
+ 'insufficient_data_actions': insufficient_data_actions,
+ 'repeat_actions': True,
+ rule_key: {
+ 'metrics': ['41869681-5776-46d6-91ed-cccc43b6e4e3',
+ 'a1fb80f4-c242-4f57-87c6-68f47521059e'],
+ 'comparison_operator': 'le',
+ 'aggregation_method': 'count',
+ 'threshold': 50,
+ 'evaluation_periods': '3',
+ 'granularity': '180',
+ }
+ }
+ self.assertRaises(
+ lib_exc.BadRequest,
+ self.alarming_client.create_alarm,
+ name=alarm_name,
+ type=adef['type'],
+ enabled=adef['enabled'],
+ state=adef['state'],
+ ok_actions=adef['ok_actions'],
+ alarm_actions=adef['alarm_actions'],
+ insufficient_data_actions=adef['insufficient_data_actions'],
+ repeat_actions=adef['repeat_actions'],
+ gnocchi_aggregation_by_metrics_threshold_rule=adef[rule_key])
+
+ @decorators.attr(type=['negative'])
+ @decorators.idempotent_id('95e505c3-51e0-4ac8-8e75-6dfb5828fcd2')
+ def test_create_invalid_alarm_ok_actions(self):
+ self._do_create_alarm_invalid_action(
+ ok_actions=['spam://something/ok'],
+ error_message='Unsupported action spam://something/ok')
+
+ @decorators.skip_because(bug="2045116")
+ @decorators.attr(type=['negative'])
+ @decorators.idempotent_id('4b2b7be0-9d13-4e36-ad28-d11c5aa06411')
+ def test_create_invalid_alarm_too_many_actions(self):
+ self._do_create_alarm_invalid_action(
+ ok_actions=['http://no.where', 'http://no.where2'],
+ error_message="alarm_actions count exceeds maximum value 1")
+
+ @decorators.attr(type=['negative'])
+ @decorators.idempotent_id('2332b637-8193-448c-8b37-e0b6a6fded34')
+ def test_post_invalid_alarm_alarm_actions(self):
+ self._do_create_alarm_invalid_action(
+ alarm_actions=['spam://something/alarm'],
+ error_message='Unsupported action spam://something/alarm')
+
+ @decorators.attr(type=['negative'])
+ @decorators.idempotent_id('78029ec7-cb70-4f89-a1b6-df235d6688b6')
+ def test_post_invalid_alarm_insufficient_data_actions(self):
+ self._do_create_alarm_invalid_action(
+ insufficient_data_actions=['spam://something/insufficient'],
+ error_message='Unsupported action spam://something/insufficient')
+
+ @decorators.attr(type=['negative'])
+ @decorators.idempotent_id('350dae2b-8720-48ae-b3a6-d92dbba55d07')
+ def test_list_invalid_sort_key(self):
+ # list with invalid sort key
+ self.assertRaises(
+ lib_exc.BadRequest,
+ self.alarming_client.list_alarms,
+ sort=['invalid_key:asc'])
+
+ @decorators.attr(type='negative')
+ @decorators.idempotent_id('32bbc39e-7423-4309-acf9-39ff584764fc')
+ def test_create_update_with_faults_delete_alarm(self):
+ # create dual actions
+ alarm_name = data_utils.rand_name('telemetry_alarm')
+ rule = {'metrics': ['41869681-5776-46d6-91ed-cccc43b6e4e3',
+ 'a1fb80f4-c242-4f57-87c6-68f47521059e'],
+ 'aggregation_method': 'mean',
+ 'comparison_operator': 'eq',
+ 'threshold': 300.0}
+ body = self.alarming_client.create_alarm(
+ name=alarm_name,
+ alarm_actions=['http://no.where'],
+ type='gnocchi_aggregation_by_metrics_threshold',
+ gnocchi_aggregation_by_metrics_threshold_rule=rule
+ )
+ alarm_id = body['alarm_id']
+ alarms = self.alarming_client.list_alarms(['name', 'eq', alarm_name])
+ self.assertEqual(1, len(alarms))
+
+ # update the alarm with wrong fields.
+ self.assertRaises(
+ lib_exc.BadRequest,
+ self.alarming_client.update_alarm,
+ alarm_id=alarm_id,
+ this_can_not_be_correct='ha')
+
+ # update the alarm with invalid action
+ self.assertRaises(
+ lib_exc.BadRequest,
+ self.alarming_client.update_alarm,
+ alarm_id=alarm_id,
+ ok_actions='spam://something/ok')
+
+ # update the alarm with invalid state
+ self.assertRaises(
+ lib_exc.BadRequest,
+ self.alarming_client.alarm_set_state,
+ alarm_id=alarm_id,
+ state='not valid')
+
+ # Delete alarm and verify if deleted
+ self.alarming_client.delete_alarm(alarm_id)
+ self.assertRaises(lib_exc.NotFound,
+ self.alarming_client.show_alarm, alarm_id)
diff --git a/telemetry_tempest_plugin/aodh/service/client.py b/telemetry_tempest_plugin/aodh/service/client.py
index 80a16f9..071bfff 100644
--- a/telemetry_tempest_plugin/aodh/service/client.py
+++ b/telemetry_tempest_plugin/aodh/service/client.py
@@ -36,15 +36,21 @@
def serialize(self, body):
return json.dumps(body)
- def list_alarms(self, query=None):
+ def list_alarms(self, query=None, sort=None, limit=None, marker=None):
uri = '%s/alarms' % self.uri_prefix
uri_dict = {}
if query:
uri_dict = {'q.field': query[0],
'q.op': query[1],
'q.value': query[2]}
+ if sort:
+ uri_dict.update({'sort': sort})
+ if limit is not None:
+ uri_dict.update({'limit': int(limit)})
+ if marker:
+ uri_dict.update({'marker': marker})
if uri_dict:
- uri += "?%s" % urllib.urlencode(uri_dict)
+ uri += "?%s" % urllib.urlencode(uri_dict, doseq=True)
resp, body = self.get(uri)
self.expected_success(200, resp.status)
body = self.deserialize(body)
@@ -103,6 +109,28 @@
body = self.deserialize(body)
return rest_client.ResponseBodyData(resp, body)
+ def invalid_path(self, headers=None):
+ uri = "invalid_path"
+ extra_headers = headers is not None
+ resp, body = self.get(uri, headers, extra_headers)
+ self.expected_success(404, resp.status)
+ body = self.deserialize(body)
+ return rest_client.ResponseBodyData(resp, body)
+
+ def show_capabilities(self):
+ uri = "%s/capabilities" % (self.uri_prefix)
+ resp, body = self.get(uri)
+ self.expected_success(200, resp.status)
+ body = self.deserialize(body)
+ return rest_client.ResponseBody(resp, body)
+
+ def show_version(self):
+ uri = '/'
+ resp, body = self.get(uri)
+ self.expected_success(200, resp.status)
+ body = self.deserialize(body)
+ return rest_client.ResponseBody(resp, body)
+
class Manager(clients.ServiceClients):