diff --git a/vitrage_tempest_tests/tests/base.py b/vitrage_tempest_tests/tests/base.py index 5d336d19f..11577b82e 100644 --- a/vitrage_tempest_tests/tests/base.py +++ b/vitrage_tempest_tests/tests/base.py @@ -76,21 +76,6 @@ class BaseVitrageTempest(base.BaseTestCase): filtered_list.append(item) return filtered_list - def _check_num_instances(self, num_instances=0, state=''): - if len(TempestClients.nova().servers.list()) != num_instances: - return False - - return all(instance.__dict__['status'].upper() == state.upper() - for instance in TempestClients.nova().servers.list()) - - def _check_num_volumes(self, num_volumes=0, state=''): - if len(TempestClients.cinder().volumes.list()) != num_volumes: - return False - - return all(volume.__dict__['status'].upper() == state.upper() and - len(volume.__dict__['attachments']) == 1 - for volume in TempestClients.cinder().volumes.list()) - def _create_graph_from_graph_dictionary(self, api_graph): self.assertIsNotNone(api_graph) graph = NXGraph() diff --git a/vitrage_tempest_tests/tests/common/general_utils.py b/vitrage_tempest_tests/tests/common/general_utils.py index 3cf35279e..13508448f 100644 --- a/vitrage_tempest_tests/tests/common/general_utils.py +++ b/vitrage_tempest_tests/tests/common/general_utils.py @@ -14,17 +14,32 @@ import six -def get_first_match(list_of_dicts, subset_dict): +def get_first_match(list_of_dicts, **kwargs): + subset_dict = _subset_dict(**kwargs) for d in list_of_dicts: if is_subset(subset_dict, d): return d -def get_all_matchs(list_of_dicts, subset_dict): +def get_all_matches(list_of_dicts, **kwargs): # TODO(idan_hefetz) this method can replace the notorious # TODO(idan_hefetz) '_filter_list_by_pairs_parameters' + subset_dict = _subset_dict(**kwargs) return [d for d in list_of_dicts if is_subset(subset_dict, d)] def is_subset(subset, full): - return six.viewitems(subset) <= six.viewitems(full) + if not subset: + return True + full_dict = full + if type(full) is not dict: + full_dict = full.__dict__ + return six.viewitems(subset) <= six.viewitems(full_dict) + + +def _subset_dict(**kwargs): + subset_dict_final = dict() + for keyword, arg in kwargs.items(): + if arg is not None: + subset_dict_final[keyword] = arg + return subset_dict_final diff --git a/vitrage_tempest_tests/tests/common/nova_utils.py b/vitrage_tempest_tests/tests/common/nova_utils.py index 755e61dd9..3d0c12e12 100644 --- a/vitrage_tempest_tests/tests/common/nova_utils.py +++ b/vitrage_tempest_tests/tests/common/nova_utils.py @@ -13,13 +13,14 @@ # under the License. import time +from vitrage_tempest_tests.tests.common import general_utils as g_utils from vitrage_tempest_tests.tests.common import glance_utils from vitrage_tempest_tests.tests.common import neutron_utils from vitrage_tempest_tests.tests.common.tempest_clients import TempestClients from vitrage_tempest_tests.tests.utils import wait_for_status -def create_instances(num_instances, set_public_network=False, name='vm'): +def create_instances(num_instances=1, set_public_network=False, name='vm'): nics = [] flavor = get_first_flavor() image = glance_utils.get_first_image() @@ -39,14 +40,18 @@ def create_instances(num_instances, set_public_network=False, name='vm'): return resources -def delete_all_instances(): +def delete_all_instances(**kwargs): instances = TempestClients.nova().servers.list() - for instance in instances: + instances_to_delete = g_utils.get_all_matches(instances, **kwargs) + for item in instances_to_delete: try: - TempestClients.nova().servers.delete(instance) + TempestClients.nova().servers.delete(item) except Exception: pass - wait_for_status(30, _check_num_instances, num_instances=0) + wait_for_status( + 30, + _check_num_instances, + num_instances=len(instances) - len(instances_to_delete)) time.sleep(2) diff --git a/vitrage_tempest_tests/tests/common/vitrage_utils.py b/vitrage_tempest_tests/tests/common/vitrage_utils.py index 15030a013..9320e4bd2 100644 --- a/vitrage_tempest_tests/tests/common/vitrage_utils.py +++ b/vitrage_tempest_tests/tests/common/vitrage_utils.py @@ -13,11 +13,11 @@ # under the License. from datetime import datetime -from vitrage.common.constants import VertexProperties as VProps from vitrage.datasources import NOVA_HOST_DATASOURCE +from vitrage.datasources import NOVA_INSTANCE_DATASOURCE from vitrage_tempest_tests.tests.api.event.base import DOWN from vitrage_tempest_tests.tests.api.event.base import UP -from vitrage_tempest_tests.tests.common.general_utils import get_first_match +from vitrage_tempest_tests.tests.common import general_utils as g_utils from vitrage_tempest_tests.tests.common.tempest_clients import TempestClients @@ -36,6 +36,13 @@ def generate_fake_host_alarm(hostname, event_type, enabled=True): TempestClients.vitrage().event.post(event_time_iso, event_type, details) -def get_first_host(): - nodes = TempestClients.vitrage().topology.get(all_tenants=True)['nodes'] - return get_first_match(nodes, {VProps.VITRAGE_TYPE: NOVA_HOST_DATASOURCE}) +def get_first_host(**kwargs): + hosts = TempestClients.vitrage().resource.list( + NOVA_HOST_DATASOURCE, all_tenants=True) + return g_utils.get_first_match(hosts, **kwargs) + + +def get_first_instance(**kwargs): + instances = TempestClients.vitrage().resource.list( + NOVA_INSTANCE_DATASOURCE, all_tenants=True) + return g_utils.get_first_match(instances, **kwargs) diff --git a/vitrage_tempest_tests/tests/e2e/test_actions_base.py b/vitrage_tempest_tests/tests/e2e/test_actions_base.py new file mode 100644 index 000000000..706398157 --- /dev/null +++ b/vitrage_tempest_tests/tests/e2e/test_actions_base.py @@ -0,0 +1,76 @@ +# Copyright 2017 - Nokia +# +# 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 time + +from oslo_log import log as logging + +from vitrage.common.constants import VertexProperties as VProps +from vitrage_tempest_tests.tests.base import BaseVitrageTempest +from vitrage_tempest_tests.tests.common import general_utils as g_utils +from vitrage_tempest_tests.tests.common.tempest_clients import TempestClients +from vitrage_tempest_tests.tests.common import vitrage_utils + +LOG = logging.getLogger(__name__) + + +class TestActionsBase(BaseVitrageTempest): + @classmethod + def setUpClass(cls): + super(TestActionsBase, cls).setUpClass() + host = vitrage_utils.get_first_host() + if not host: + raise Exception("No host found") + if not host.get(VProps.VITRAGE_AGGREGATED_STATE) == 'AVAILABLE': + raise Exception("Host is not running %s", str(host)) + cls.orig_host = host + + def _trigger_do_action(self, trigger_name): + vitrage_utils.generate_fake_host_alarm( + self.orig_host.get('name'), + enabled=True, + event_type=trigger_name + ) + time.sleep(2) + + def _trigger_undo_action(self, trigger_name): + vitrage_utils.generate_fake_host_alarm( + self.orig_host.get('name'), + enabled=False, + event_type=trigger_name + ) + time.sleep(2) + + def _check_deduced(self, deduced_count, deduced_props, resource_id): + alarms = TempestClients.vitrage().alarm.list( + vitrage_id=resource_id, + all_tenants=True) + deduces = g_utils.get_all_matches(alarms, **deduced_props) + self.assertEqual( + deduced_count, + len(deduces), + 'Expected %s deduces\n - \n%s\n - \n%s' % + (str(deduced_count), str(alarms), str(deduces))) + + def _check_rca(self, rca, expected_alarms, inspected): + self.assertEqual(len(expected_alarms), len(rca['nodes'])) + for expected_alarm in expected_alarms: + self.assertIsNotNone( + g_utils.get_first_match(rca['nodes'], **expected_alarm), + 'expected_alarm is not in the rca %s' % str(expected_alarm)) + rca_inspected = rca['nodes'][rca['inspected_index']] + self.assertEqual( + True, + g_utils.is_subset(inspected, rca_inspected), + 'Invalid inspected item \n%s\n%s' % + (str(rca_inspected), str(inspected))) diff --git a/vitrage_tempest_tests/tests/e2e/test_basic_actions.py b/vitrage_tempest_tests/tests/e2e/test_basic_actions.py index aa610e9d9..e0bb946e2 100644 --- a/vitrage_tempest_tests/tests/e2e/test_basic_actions.py +++ b/vitrage_tempest_tests/tests/e2e/test_basic_actions.py @@ -20,16 +20,19 @@ from vitrage.common.constants import VertexProperties as VProps from vitrage.datasources.doctor import DOCTOR_DATASOURCE from vitrage.evaluator.actions.evaluator_event_transformer import \ VITRAGE_DATASOURCE -from vitrage_tempest_tests.tests.base import BaseVitrageTempest from vitrage_tempest_tests.tests.common import general_utils as g_utils +from vitrage_tempest_tests.tests.common import nova_utils from vitrage_tempest_tests.tests.common.tempest_clients import TempestClients from vitrage_tempest_tests.tests.common import vitrage_utils +from vitrage_tempest_tests.tests.e2e.test_actions_base import TestActionsBase from vitrage_tempest_tests.tests import utils LOG = logging.getLogger(__name__) TRIGGER_ALARM_1 = 'e2e.test_basic_actions.trigger.alarm1' TRIGGER_ALARM_2 = 'e2e.test_basic_actions.trigger.alarm2' +TRIGGER_ALARM_3 = 'e2e.test_basic_actions.trigger.alarm3' +TRIGGER_ALARM_4 = 'e2e.test_basic_actions.trigger.alarm4' DEDUCED = 'e2e.test_basic_actions.deduced.alarm' TRIGGER_ALARM_2_PROPS = { @@ -45,35 +48,9 @@ DEDUCED_PROPS = { } -class TestBasicActions(BaseVitrageTempest): - @classmethod - def setUpClass(cls): - super(TestBasicActions, cls).setUpClass() - host = vitrage_utils.get_first_host() - if not host: - raise Exception("No host found") - if not host.get(VProps.VITRAGE_AGGREGATED_STATE) == 'AVAILABLE': - raise Exception("Host is not running %s", str(host)) - cls.orig_host = host - - def _trigger_do_action(self, trigger_name): - vitrage_utils.generate_fake_host_alarm( - self.orig_host.get('name'), - enabled=True, - event_type=trigger_name - ) - time.sleep(2) - - def _trigger_undo_action(self, trigger_name): - vitrage_utils.generate_fake_host_alarm( - self.orig_host.get('name'), - enabled=False, - event_type=trigger_name - ) - time.sleep(2) - +class TestBasicActions(TestActionsBase): @utils.tempest_logger - def test_action_set_state(self): + def test_action_set_state_host(self): try: # Do @@ -90,26 +67,56 @@ class TestBasicActions(BaseVitrageTempest): self.assertEqual( self.orig_host.get(VProps.VITRAGE_AGGREGATED_STATE), curr_host.get(VProps.VITRAGE_AGGREGATED_STATE), - 'state should change after set_state action') + 'state should change after undo set_state action') except Exception as e: self._handle_exception(e) raise finally: self._trigger_undo_action(TRIGGER_ALARM_1) + @utils.tempest_logger + def test_action_set_state_instance(self): + + vm_id = "" + try: + vm_id = nova_utils.create_instances(set_public_network=True)[0].id + + # Do + orig_instance = vitrage_utils.get_first_instance(id=vm_id) + self._trigger_do_action(TRIGGER_ALARM_3) + curr_instance = vitrage_utils.get_first_instance(id=vm_id) + self.assertEqual( + 'ERROR', + curr_instance.get(VProps.VITRAGE_AGGREGATED_STATE), + 'state should change after set_state action') + + # Undo + self._trigger_undo_action(TRIGGER_ALARM_3) + curr_instance = vitrage_utils.get_first_instance(id=vm_id) + self.assertEqual( + orig_instance.get(VProps.VITRAGE_AGGREGATED_STATE), + curr_instance.get(VProps.VITRAGE_AGGREGATED_STATE), + 'state should change after undo set_state action') + except Exception as e: + self._handle_exception(e) + raise + finally: + self._trigger_undo_action(TRIGGER_ALARM_3) + nova_utils.delete_all_instances(id=vm_id) + @utils.tempest_logger def test_action_mark_down_host(self): try: host_name = self.orig_host.get(VProps.NAME) # Do - self._trigger_do_action(TRIGGER_ALARM_1) + self._trigger_do_action(TRIGGER_ALARM_4) nova_service = TempestClients.nova().services.list( host=host_name, binary='nova-compute')[0] self.assertEqual("down", str(nova_service.state)) # Undo - self._trigger_undo_action(TRIGGER_ALARM_1) + self._trigger_undo_action(TRIGGER_ALARM_4) nova_service = TempestClients.nova().services.list( host=host_name, binary='nova-compute')[0] self.assertEqual("up", str(nova_service.state)) @@ -117,7 +124,31 @@ class TestBasicActions(BaseVitrageTempest): self._handle_exception(e) raise finally: - self._trigger_undo_action(TRIGGER_ALARM_1) + self._trigger_undo_action(TRIGGER_ALARM_4) + # nova.host datasource may take up to snapshot_intreval to update + time.sleep(130) + + @utils.tempest_logger + def test_action_mark_down_instance(self): + vm_id = "" + try: + vm_id = nova_utils.create_instances(set_public_network=True)[0].id + # Do + self._trigger_do_action(TRIGGER_ALARM_3) + nova_instance = TempestClients.nova().servers.get(vm_id) + self.assertEqual("ERROR", str(nova_instance.status)) + + # Undo + self._trigger_undo_action(TRIGGER_ALARM_3) + nova_instance = TempestClients.nova().servers.get(vm_id) + self.assertEqual("ACTIVE", str(nova_instance.status)) + except Exception as e: + self._handle_exception(e) + raise + finally: + pass + self._trigger_undo_action(TRIGGER_ALARM_3) + nova_utils.delete_all_instances(id=vm_id) @utils.tempest_logger def test_action_deduce_alarm(self): @@ -137,17 +168,6 @@ class TestBasicActions(BaseVitrageTempest): finally: self._trigger_undo_action(TRIGGER_ALARM_2) - def _check_deduced(self, deduced_count, deduced_props, resource_id): - alarms = TempestClients.vitrage().alarm.list( - vitrage_id=resource_id, - all_tenants=True) - deduces = g_utils.get_all_matchs(alarms, deduced_props) - self.assertEqual( - deduced_count, - len(deduces), - 'Expected %s deduces\n - \n%s\n - \n%s' % - (str(deduced_count), str(alarms), str(deduces))) - @utils.tempest_logger def test_action_add_causal_relationship(self): try: @@ -156,9 +176,10 @@ class TestBasicActions(BaseVitrageTempest): alarms = TempestClients.vitrage().alarm.list( vitrage_id=self.orig_host.get(VProps.VITRAGE_ID), all_tenants=True) + self.assertEqual(True, len(alarms) >= 2, 'alarms %s' % str(alarms)) - deduced = g_utils.get_all_matchs(alarms, DEDUCED_PROPS)[0] - trigger = g_utils.get_all_matchs(alarms, TRIGGER_ALARM_2_PROPS)[0] + deduced = g_utils.get_first_match(alarms, **DEDUCED_PROPS) + trigger = g_utils.get_first_match(alarms, **TRIGGER_ALARM_2_PROPS) # Get Rca for the deduced rca = TempestClients.vitrage().rca.get( @@ -174,16 +195,3 @@ class TestBasicActions(BaseVitrageTempest): raise finally: self._trigger_undo_action(TRIGGER_ALARM_2) - - def _check_rca(self, rca, expected_alarms, inspected): - self.assertEqual(len(expected_alarms), len(rca['nodes'])) - for expected_alarm in expected_alarms: - self.assertIsNotNone( - g_utils.get_first_match(rca['nodes'], expected_alarm), - 'expected_alarm is not in the rca %s' % str(expected_alarm)) - rca_inspected = rca['nodes'][rca['inspected_index']] - self.assertEqual( - True, - g_utils.is_subset(inspected, rca_inspected), - 'Invalid inspected item \n%s\n%s' % - (str(rca_inspected), str(inspected))) diff --git a/vitrage_tempest_tests/tests/e2e/test_overlapping_actions.py b/vitrage_tempest_tests/tests/e2e/test_overlapping_actions.py new file mode 100644 index 000000000..102f2ae90 --- /dev/null +++ b/vitrage_tempest_tests/tests/e2e/test_overlapping_actions.py @@ -0,0 +1,234 @@ +# Copyright 2017 - Nokia +# +# 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 time + +from oslo_log import log as logging + +from vitrage.common.constants import EntityCategory +from vitrage.common.constants import VertexProperties as VProps +from vitrage.datasources.doctor import DOCTOR_DATASOURCE +from vitrage.evaluator.actions.evaluator_event_transformer import \ + VITRAGE_DATASOURCE +from vitrage_tempest_tests.tests.common import general_utils as g_utils +from vitrage_tempest_tests.tests.common.tempest_clients import TempestClients +from vitrage_tempest_tests.tests.common import vitrage_utils +from vitrage_tempest_tests.tests.e2e.test_actions_base import TestActionsBase +from vitrage_tempest_tests.tests import utils + +LOG = logging.getLogger(__name__) + +TRIGGER_ALARM_1 = 'e2e.test_overlapping_actions.trigger.alarm1' +TRIGGER_ALARM_2 = 'e2e.test_overlapping_actions.trigger.alarm2' +TRIGGER_ALARM_3 = 'e2e.test_overlapping_actions.trigger.alarm3' +TRIGGER_ALARM_4 = 'e2e.test_overlapping_actions.trigger.alarm4' +DEDUCED = 'e2e.test_overlapping_actions.deduced.alarm' + +TRIGGER_ALARM_1_PROPS = { + VProps.NAME: TRIGGER_ALARM_1, + VProps.VITRAGE_CATEGORY: EntityCategory.ALARM, + VProps.VITRAGE_TYPE: DOCTOR_DATASOURCE, +} + +TRIGGER_ALARM_2_PROPS = { + VProps.NAME: TRIGGER_ALARM_2, + VProps.VITRAGE_CATEGORY: EntityCategory.ALARM, + VProps.VITRAGE_TYPE: DOCTOR_DATASOURCE, +} + +DEDUCED_PROPS = { + VProps.NAME: DEDUCED, + VProps.VITRAGE_CATEGORY: EntityCategory.ALARM, + VProps.VITRAGE_TYPE: VITRAGE_DATASOURCE, +} + + +class TestOverlappingcActions(TestActionsBase): + @utils.tempest_logger + def test_overlapping_action_set_state(self): + try: + # Do - first + self._trigger_do_action(TRIGGER_ALARM_1) + curr_host = vitrage_utils.get_first_host() + self.assertEqual( + 'ERROR', + curr_host.get(VProps.VITRAGE_AGGREGATED_STATE), + 'state should change after set_state action') + + # Do - second + self._trigger_do_action(TRIGGER_ALARM_2) + curr_host = vitrage_utils.get_first_host() + self.assertEqual( + 'ERROR', + curr_host.get(VProps.VITRAGE_AGGREGATED_STATE), + 'state should remain unchanged') + + # Undo - first + self._trigger_undo_action(TRIGGER_ALARM_1) + curr_host = vitrage_utils.get_first_host() + self.assertEqual( + 'ERROR', + curr_host.get(VProps.VITRAGE_AGGREGATED_STATE), + 'state should remain unchanged') + + # Undo - second + self._trigger_undo_action(TRIGGER_ALARM_2) + curr_host = vitrage_utils.get_first_host() + self.assertEqual( + self.orig_host.get(VProps.VITRAGE_AGGREGATED_STATE), + curr_host.get(VProps.VITRAGE_AGGREGATED_STATE), + 'state should change after undo set_state action') + + except Exception as e: + self._handle_exception(e) + raise + finally: + self._trigger_undo_action(TRIGGER_ALARM_1) + self._trigger_undo_action(TRIGGER_ALARM_2) + + @utils.tempest_logger + def test_overlapping_action_mark_down(self): + try: + host_name = self.orig_host.get(VProps.NAME) + + # Do - first + self._trigger_do_action(TRIGGER_ALARM_3) + nova_service = TempestClients.nova().services.list( + host=host_name, binary='nova-compute')[0] + self.assertEqual("down", str(nova_service.state)) + + # Do - second + self._trigger_do_action(TRIGGER_ALARM_4) + nova_service = TempestClients.nova().services.list( + host=host_name, binary='nova-compute')[0] + self.assertEqual("down", str(nova_service.state)) + + # Undo - first + self._trigger_undo_action(TRIGGER_ALARM_3) + nova_service = TempestClients.nova().services.list( + host=host_name, binary='nova-compute')[0] + self.assertEqual("down", str(nova_service.state)) + + # Undo - second + self._trigger_undo_action(TRIGGER_ALARM_4) + nova_service = TempestClients.nova().services.list( + host=host_name, binary='nova-compute')[0] + self.assertEqual("up", str(nova_service.state)) + except Exception as e: + self._handle_exception(e) + raise + finally: + self._trigger_undo_action(TRIGGER_ALARM_3) + self._trigger_undo_action(TRIGGER_ALARM_4) + # nova.host datasource may take up to snapshot_intreval to update + time.sleep(130) + + @utils.tempest_logger + def test_overlapping_action_deduce_alarm(self): + try: + host_id = self.orig_host.get(VProps.VITRAGE_ID) + + # Do - first + self._trigger_do_action(TRIGGER_ALARM_1) + self._check_deduced(1, DEDUCED_PROPS, host_id) + + # Do - second + self._trigger_do_action(TRIGGER_ALARM_2) + self._check_deduced(1, DEDUCED_PROPS, host_id) + + # Undo - first + self._trigger_undo_action(TRIGGER_ALARM_1) + self._check_deduced(1, DEDUCED_PROPS, host_id) + + # Undo - second + self._trigger_undo_action(TRIGGER_ALARM_2) + self._check_deduced(0, DEDUCED_PROPS, host_id) + except Exception as e: + self._handle_exception(e) + raise + finally: + self._trigger_undo_action(TRIGGER_ALARM_1) + self._trigger_undo_action(TRIGGER_ALARM_2) + + @utils.tempest_logger + def test_overlapping_action_add_causal_relationship(self): + try: + # ---- Do first & second ---- + self._trigger_do_action(TRIGGER_ALARM_1) + self._trigger_do_action(TRIGGER_ALARM_2) + alarms = TempestClients.vitrage().alarm.list( + vitrage_id=self.orig_host.get(VProps.VITRAGE_ID), + all_tenants=True) + + deduced = g_utils.get_first_match(alarms, **DEDUCED_PROPS) + trigger1 = g_utils.get_first_match(alarms, **TRIGGER_ALARM_1_PROPS) + trigger2 = g_utils.get_first_match(alarms, **TRIGGER_ALARM_2_PROPS) + + # Get Rca for the deduced + rca = TempestClients.vitrage().rca.get( + deduced[VProps.VITRAGE_ID], all_tenants=True) + self._check_rca(rca, [deduced, trigger1, trigger2], DEDUCED_PROPS) + + # Get Rca for trigger 1 + rca = TempestClients.vitrage().rca.get( + trigger1[VProps.VITRAGE_ID], all_tenants=True) + self._check_rca(rca, [deduced, trigger1], TRIGGER_ALARM_1_PROPS) + + # Get Rca for trigger 2 + rca = TempestClients.vitrage().rca.get( + trigger2[VProps.VITRAGE_ID], all_tenants=True) + self._check_rca(rca, [deduced, trigger2], TRIGGER_ALARM_2_PROPS) + + # ---- Undo - first ---- + self._trigger_undo_action(TRIGGER_ALARM_1) + alarms = TempestClients.vitrage().alarm.list( + vitrage_id=self.orig_host.get(VProps.VITRAGE_ID), + all_tenants=True) + + deduced = g_utils.get_first_match(alarms, **DEDUCED_PROPS) + trigger2 = g_utils.get_first_match(alarms, **TRIGGER_ALARM_2_PROPS) + + # Get Rca for the deduced + rca = TempestClients.vitrage().rca.get( + deduced[VProps.VITRAGE_ID], all_tenants=True) + self._check_rca(rca, [deduced, trigger2], DEDUCED_PROPS) + + # Get Rca for trigger 2 + rca = TempestClients.vitrage().rca.get( + trigger2[VProps.VITRAGE_ID], all_tenants=True) + self._check_rca(rca, [deduced, trigger2], TRIGGER_ALARM_2_PROPS) + + # ---- Undo - second ---- + self._trigger_undo_action(TRIGGER_ALARM_2) + alarms = TempestClients.vitrage().alarm.list( + vitrage_id=self.orig_host.get(VProps.VITRAGE_ID), + all_tenants=True) + self.assertEqual( + 0, + len(g_utils.get_all_matches(alarms, **TRIGGER_ALARM_1_PROPS)), + 'trigger alarm 1 should have been removed') + self.assertEqual( + 0, + len(g_utils.get_all_matches(alarms, **TRIGGER_ALARM_2_PROPS)), + 'trigger alarm 2 should have been removed') + self.assertEqual( + 0, + len(g_utils.get_all_matches(alarms, **DEDUCED_PROPS)), + 'deduced alarm should have been removed') + + except Exception as e: + self._handle_exception(e) + raise + finally: + self._trigger_undo_action(TRIGGER_ALARM_1) + self._trigger_undo_action(TRIGGER_ALARM_2) diff --git a/vitrage_tempest_tests/tests/resources/templates/api/e2e_test_basic_actions.yaml b/vitrage_tempest_tests/tests/resources/templates/api/e2e_test_basic_actions.yaml index 04691e3ce..1cad96c27 100644 --- a/vitrage_tempest_tests/tests/resources/templates/api/e2e_test_basic_actions.yaml +++ b/vitrage_tempest_tests/tests/resources/templates/api/e2e_test_basic_actions.yaml @@ -11,6 +11,14 @@ definitions: category: ALARM name: e2e.test_basic_actions.trigger.alarm2 template_id: trigger_alarm_2 + - entity: + category: ALARM + name: e2e.test_basic_actions.trigger.alarm3 + template_id: trigger_alarm_3 + - entity: + category: ALARM + name: e2e.test_basic_actions.trigger.alarm4 + template_id: trigger_alarm_4 - entity: category: ALARM type: vitrage @@ -20,6 +28,10 @@ definitions: category: RESOURCE type: nova.host template_id: host + - entity: + category: RESOURCE + type: nova.instance + template_id: instance relationships: - relationship: source: trigger_alarm_1 @@ -31,11 +43,26 @@ definitions: relationship_type: on target: host template_id : trigger_alarm_2_on_host + - relationship: + source: trigger_alarm_3 + relationship_type: on + target: host + template_id : trigger_alarm_3_on_host + - relationship: + source: trigger_alarm_4 + relationship_type: on + target: host + template_id : trigger_alarm_4_on_host - relationship: source: deduced_alarm relationship_type: on target: host template_id : deduced_alarm_on_host + - relationship: + source: host + target: instance + relationship_type: contains + template_id: host_contains_instance scenarios: - scenario: condition: trigger_alarm_1_on_host @@ -46,6 +73,9 @@ scenarios: target: host properties: state: ERROR + - scenario: + condition: trigger_alarm_4_on_host + actions: - action: action_type: mark_down action_target: @@ -68,3 +98,16 @@ scenarios: action_target: source: trigger_alarm_2 target: deduced_alarm + - scenario: + condition: trigger_alarm_3_on_host and host_contains_instance + actions: + - action: + action_type: set_state + action_target: + target: instance + properties: + state: ERROR + - action: + action_type: mark_down + action_target: + target: instance diff --git a/vitrage_tempest_tests/tests/resources/templates/api/e2e_test_overlapping_actions.yaml b/vitrage_tempest_tests/tests/resources/templates/api/e2e_test_overlapping_actions.yaml new file mode 100644 index 000000000..edc13fea5 --- /dev/null +++ b/vitrage_tempest_tests/tests/resources/templates/api/e2e_test_overlapping_actions.yaml @@ -0,0 +1,131 @@ +metadata: + name: e2e_test_overlapping_actions + description: this template includes vitrage basic actions +definitions: + entities: + - entity: + category: ALARM + name: e2e.test_overlapping_actions.trigger.alarm1 + template_id: trigger_alarm_1 + - entity: + category: ALARM + name: e2e.test_overlapping_actions.trigger.alarm2 + template_id: trigger_alarm_2 + - entity: + category: ALARM + name: e2e.test_overlapping_actions.trigger.alarm3 + template_id: trigger_alarm_3 + - entity: + category: ALARM + name: e2e.test_overlapping_actions.trigger.alarm4 + template_id: trigger_alarm_4 + - entity: + category: ALARM + type: vitrage + name: e2e.test_overlapping_actions.deduced.alarm + template_id: deduced_alarm + - entity: + category: RESOURCE + type: nova.host + template_id: host + relationships: + - relationship: + source: trigger_alarm_1 + relationship_type: on + target: host + template_id : trigger_alarm_1_on_host + - relationship: + source: trigger_alarm_2 + relationship_type: on + target: host + template_id : trigger_alarm_2_on_host + - relationship: + source: trigger_alarm_3 + relationship_type: on + target: host + template_id : trigger_alarm_3_on_host + - relationship: + source: trigger_alarm_4 + relationship_type: on + target: host + template_id : trigger_alarm_4_on_host + - relationship: + source: deduced_alarm + relationship_type: on + target: host + template_id : deduced_alarm_on_host +scenarios: + +# First part - trigger_alarm_1_on_host or trigger_alarm_3_on_host: + + - scenario: + condition: trigger_alarm_1_on_host + actions: + - action: + action_type: set_state + action_target: + target: host + properties: + state: ERROR + - scenario: + condition: trigger_alarm_3_on_host + actions: + - action: + action_type: mark_down + action_target: + target: host + - scenario: + condition: trigger_alarm_1_on_host + actions: + - action: + action_type: raise_alarm + action_target: + target: host + properties: + alarm_name: e2e.test_overlapping_actions.deduced.alarm + severity: WARNING + - scenario: + condition: trigger_alarm_1_on_host and deduced_alarm_on_host + actions: + - action: + action_type: add_causal_relationship + action_target: + source: trigger_alarm_1 + target: deduced_alarm + +# Second part - trigger_alarm_2_on_host or trigger_alarm_4_on_host: + + - scenario: + condition: trigger_alarm_2_on_host + actions: + - action: + action_type: set_state + action_target: + target: host + properties: + state: ERROR + - scenario: + condition: trigger_alarm_4_on_host + actions: + - action: + action_type: mark_down + action_target: + target: host + - scenario: + condition: trigger_alarm_2_on_host + actions: + - action: + action_type: raise_alarm + action_target: + target: host + properties: + alarm_name: e2e.test_overlapping_actions.deduced.alarm + severity: WARNING + - scenario: + condition: trigger_alarm_2_on_host and deduced_alarm_on_host + actions: + - action: + action_type: add_causal_relationship + action_target: + source: trigger_alarm_2 + target: deduced_alarm