fix bugs: Merging actions by action_spec should be moved after action_tracker
We have created the following template: (alarm_a or alarm_b) and alarm_c cause alarm_d event sequence: 1. alarm_a is reported 2. alarm_b is reported 3. alarm_c is reported --> alarm_d is raised 4. alarm_b is removed --> alarm_d is removed wrongly Change-Id: I3f9795a581eaa4fdd7c0cd7fc859f870b7c503fb
This commit is contained in:
parent
955aa5a8db
commit
e78235e0f6
@ -100,14 +100,14 @@ class ScenarioEvaluator(object):
|
|||||||
actions = self._process_and_get_actions(before,
|
actions = self._process_and_get_actions(before,
|
||||||
before_scenarios,
|
before_scenarios,
|
||||||
ActionMode.UNDO)
|
ActionMode.UNDO)
|
||||||
actions.update(self._process_and_get_actions(current,
|
actions.extend(self._process_and_get_actions(current,
|
||||||
current_scenarios,
|
current_scenarios,
|
||||||
ActionMode.DO))
|
ActionMode.DO))
|
||||||
|
|
||||||
if actions:
|
if actions:
|
||||||
LOG.debug("Actions to perform: %s", actions.values())
|
LOG.debug("Actions to perform: %s", actions)
|
||||||
filtered_actions = \
|
filtered_actions = \
|
||||||
self._analyze_and_filter_actions(actions.values())
|
self._analyze_and_filter_actions(actions)
|
||||||
LOG.debug("Actions filtered: %s", filtered_actions)
|
LOG.debug("Actions filtered: %s", filtered_actions)
|
||||||
for action in filtered_actions:
|
for action in filtered_actions:
|
||||||
self._action_executor.execute(action.specs, action.mode)
|
self._action_executor.execute(action.specs, action.mode)
|
||||||
@ -139,12 +139,12 @@ class ScenarioEvaluator(object):
|
|||||||
return before, current
|
return before, current
|
||||||
|
|
||||||
def _process_and_get_actions(self, element, triggered_scenarios, mode):
|
def _process_and_get_actions(self, element, triggered_scenarios, mode):
|
||||||
actions = {}
|
actions = []
|
||||||
for triggered_scenario in triggered_scenarios:
|
for triggered_scenario in triggered_scenarios:
|
||||||
LOG.debug("Processing: %s", str(triggered_scenario))
|
LOG.debug("Processing: %s", str(triggered_scenario))
|
||||||
scenario_element = triggered_scenario[0]
|
scenario_element = triggered_scenario[0]
|
||||||
scenario = triggered_scenario[1]
|
scenario = triggered_scenario[1]
|
||||||
actions.update(self._process_scenario(element,
|
actions.extend(self._process_scenario(element,
|
||||||
scenario,
|
scenario,
|
||||||
scenario_element,
|
scenario_element,
|
||||||
mode))
|
mode))
|
||||||
@ -153,7 +153,7 @@ class ScenarioEvaluator(object):
|
|||||||
def _process_scenario(self, element, scenario, scenario_elements, mode):
|
def _process_scenario(self, element, scenario, scenario_elements, mode):
|
||||||
if not isinstance(scenario_elements, list):
|
if not isinstance(scenario_elements, list):
|
||||||
scenario_elements = [scenario_elements]
|
scenario_elements = [scenario_elements]
|
||||||
actions = {}
|
actions = []
|
||||||
for action in scenario.actions:
|
for action in scenario.actions:
|
||||||
for scenario_element in scenario_elements:
|
for scenario_element in scenario_elements:
|
||||||
matches = self._evaluate_subgraphs(scenario.subgraphs,
|
matches = self._evaluate_subgraphs(scenario.subgraphs,
|
||||||
@ -161,7 +161,7 @@ class ScenarioEvaluator(object):
|
|||||||
scenario_element,
|
scenario_element,
|
||||||
action.targets['target'])
|
action.targets['target'])
|
||||||
|
|
||||||
actions.update(self._get_actions_from_matches(matches,
|
actions.extend(self._get_actions_from_matches(matches,
|
||||||
mode,
|
mode,
|
||||||
action,
|
action,
|
||||||
scenario))
|
scenario))
|
||||||
@ -189,7 +189,7 @@ class ScenarioEvaluator(object):
|
|||||||
mode,
|
mode,
|
||||||
action_spec,
|
action_spec,
|
||||||
scenario):
|
scenario):
|
||||||
actions = {}
|
actions = []
|
||||||
for is_switch_mode, matches in combined_matches:
|
for is_switch_mode, matches in combined_matches:
|
||||||
new_mode = mode
|
new_mode = mode
|
||||||
if is_switch_mode:
|
if is_switch_mode:
|
||||||
@ -197,11 +197,11 @@ class ScenarioEvaluator(object):
|
|||||||
if mode == ActionMode.DO else ActionMode.DO
|
if mode == ActionMode.DO else ActionMode.DO
|
||||||
|
|
||||||
for match in matches:
|
for match in matches:
|
||||||
spec, action_id = self._get_action_spec(action_spec, match)
|
spec = self._get_action_spec(action_spec, match)
|
||||||
items_ids = [match[1].vertex_id for match in match.items()]
|
items_ids = [match[1].vertex_id for match in match.items()]
|
||||||
match_hash = hash(tuple(sorted(items_ids)))
|
match_hash = hash(tuple(sorted(items_ids)))
|
||||||
actions[action_id] = ActionInfo(spec, new_mode,
|
actions.append(ActionInfo(spec, new_mode,
|
||||||
scenario.id, match_hash)
|
scenario.id, match_hash))
|
||||||
|
|
||||||
return actions
|
return actions
|
||||||
|
|
||||||
@ -211,12 +211,9 @@ class ScenarioEvaluator(object):
|
|||||||
real_items = {
|
real_items = {
|
||||||
target: match[target_id] for target, target_id in targets.items()
|
target: match[target_id] for target, target_id in targets.items()
|
||||||
}
|
}
|
||||||
revised_spec = ActionSpecs(action_spec.type,
|
return ActionSpecs(action_spec.type,
|
||||||
real_items,
|
real_items,
|
||||||
action_spec.properties)
|
action_spec.properties)
|
||||||
# noinspection PyTypeChecker
|
|
||||||
action_id = ScenarioEvaluator._generate_action_id(revised_spec)
|
|
||||||
return revised_spec, action_id
|
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _generate_action_id(action_spec):
|
def _generate_action_id(action_spec):
|
||||||
@ -248,7 +245,12 @@ class ScenarioEvaluator(object):
|
|||||||
actions_to_perform[key] = undo_action
|
actions_to_perform[key] = undo_action
|
||||||
elif new_dominant != prev_dominant:
|
elif new_dominant != prev_dominant:
|
||||||
actions_to_perform[key] = new_dominant
|
actions_to_perform[key] = new_dominant
|
||||||
return actions_to_perform.values()
|
|
||||||
|
# filter the same action
|
||||||
|
final_actions = {ScenarioEvaluator._generate_action_id(action.specs):
|
||||||
|
action for action in actions_to_perform.values()}
|
||||||
|
|
||||||
|
return final_actions.values()
|
||||||
|
|
||||||
def _find_vertex_subgraph_matching(self,
|
def _find_vertex_subgraph_matching(self,
|
||||||
subgraphs,
|
subgraphs,
|
||||||
|
@ -1145,6 +1145,126 @@ class TestScenarioEvaluator(TestFunctionalBase):
|
|||||||
alarms = self._get_alarms_on_host(host_v, processor.entity_graph)
|
alarms = self._get_alarms_on_host(host_v, processor.entity_graph)
|
||||||
self.assertEqual(0, len(alarms))
|
self.assertEqual(0, len(alarms))
|
||||||
|
|
||||||
|
def test_both_and_or_operator_for_tracker(self):
|
||||||
|
"""(alarm_a or alarm_b) and alarm_c use case
|
||||||
|
|
||||||
|
We have created the following template:
|
||||||
|
(alarm_a or alarm_b) and alarm_c cause alarm_d
|
||||||
|
|
||||||
|
1. alarm_a is reported
|
||||||
|
2. alarm_b is reported
|
||||||
|
3. alarm_c is reported --> alarm_d is raised
|
||||||
|
4. alarm_b is removed --> alarm_d should not be removed
|
||||||
|
5. alarm_a is removed --> alarm_d should be removed
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
event_queue, processor, evaluator = self._init_system()
|
||||||
|
entity_graph = processor.entity_graph
|
||||||
|
|
||||||
|
# constants
|
||||||
|
num_orig_vertices = entity_graph.num_vertices()
|
||||||
|
num_orig_edges = entity_graph.num_edges()
|
||||||
|
|
||||||
|
host_v = self._get_entity_from_graph(NOVA_HOST_DATASOURCE,
|
||||||
|
_TARGET_HOST,
|
||||||
|
_TARGET_HOST,
|
||||||
|
entity_graph)
|
||||||
|
self.assertEqual('AVAILABLE', host_v[VProps.AGGREGATED_STATE],
|
||||||
|
'host should be AVAILABLE when starting')
|
||||||
|
|
||||||
|
# generate nagios alarm_a to trigger
|
||||||
|
test_vals = {'status': 'WARNING',
|
||||||
|
'service': 'alarm_a'}
|
||||||
|
test_vals.update(_NAGIOS_TEST_INFO)
|
||||||
|
generator = mock_driver.simple_nagios_alarm_generators(1, 1, test_vals)
|
||||||
|
alarm_a_test = mock_driver.generate_random_events_list(generator)[0]
|
||||||
|
|
||||||
|
host_v = self.get_host_after_event(event_queue, alarm_a_test,
|
||||||
|
processor, _TARGET_HOST)
|
||||||
|
alarms = self._get_alarms_on_host(host_v, entity_graph)
|
||||||
|
self.assertEqual(1, len(alarms))
|
||||||
|
self.assertEqual(num_orig_vertices + 1, entity_graph.num_vertices())
|
||||||
|
self.assertEqual(num_orig_edges + 1, entity_graph.num_edges())
|
||||||
|
|
||||||
|
# generate nagios alarm_b to trigger
|
||||||
|
test_vals = {'status': 'WARNING',
|
||||||
|
'service': 'alarm_b'}
|
||||||
|
test_vals.update(_NAGIOS_TEST_INFO)
|
||||||
|
generator = mock_driver.simple_nagios_alarm_generators(1, 1, test_vals)
|
||||||
|
alarm_b_test = mock_driver.generate_random_events_list(generator)[0]
|
||||||
|
|
||||||
|
host_v = self.get_host_after_event(event_queue, alarm_b_test,
|
||||||
|
processor, _TARGET_HOST)
|
||||||
|
alarms = self._get_alarms_on_host(host_v, entity_graph)
|
||||||
|
self.assertEqual(2, len(alarms))
|
||||||
|
self.assertEqual(num_orig_vertices + 2, entity_graph.num_vertices())
|
||||||
|
self.assertEqual(num_orig_edges + 2, entity_graph.num_edges())
|
||||||
|
|
||||||
|
# generate nagios alarm_c to trigger, alarm_d is raised
|
||||||
|
test_vals = {'status': 'WARNING',
|
||||||
|
'service': 'alarm_c'}
|
||||||
|
test_vals.update(_NAGIOS_TEST_INFO)
|
||||||
|
generator = mock_driver.simple_nagios_alarm_generators(1, 1, test_vals)
|
||||||
|
alarm_c_test = mock_driver.generate_random_events_list(generator)[0]
|
||||||
|
|
||||||
|
host_v = self.get_host_after_event(event_queue, alarm_c_test,
|
||||||
|
processor, _TARGET_HOST)
|
||||||
|
alarms = self._get_alarms_on_host(host_v, entity_graph)
|
||||||
|
self.assertEqual(4, len(alarms))
|
||||||
|
self.assertEqual(num_orig_vertices + 4, entity_graph.num_vertices())
|
||||||
|
self.assertEqual(num_orig_edges + 4, entity_graph.num_edges())
|
||||||
|
|
||||||
|
# remove nagios alarm_b, alarm_d should not be removed
|
||||||
|
test_vals = {'status': 'OK',
|
||||||
|
'service': 'alarm_b'}
|
||||||
|
test_vals.update(_NAGIOS_TEST_INFO)
|
||||||
|
generator = mock_driver.simple_nagios_alarm_generators(1, 1, test_vals)
|
||||||
|
alarm_b_ok = mock_driver.generate_random_events_list(generator)[0]
|
||||||
|
|
||||||
|
host_v = self.get_host_after_event(event_queue, alarm_b_ok,
|
||||||
|
processor, _TARGET_HOST)
|
||||||
|
alarms = self._get_alarms_on_host(host_v, entity_graph)
|
||||||
|
self.assertEqual(3, len(alarms))
|
||||||
|
|
||||||
|
query = {VProps.CATEGORY: EntityCategory.ALARM,
|
||||||
|
VProps.IS_DELETED: True}
|
||||||
|
deleted_alarms = entity_graph.neighbors(host_v.vertex_id,
|
||||||
|
vertex_attr_filter=query)
|
||||||
|
self.assertEqual(num_orig_vertices + len(deleted_alarms) + 3,
|
||||||
|
entity_graph.num_vertices())
|
||||||
|
|
||||||
|
query = {VProps.IS_DELETED: True}
|
||||||
|
deleted_edges = entity_graph.neighbors(host_v.vertex_id,
|
||||||
|
edge_attr_filter=query)
|
||||||
|
self.assertEqual(num_orig_edges + len(deleted_edges) + 3,
|
||||||
|
entity_graph.num_edges())
|
||||||
|
|
||||||
|
# remove nagios alarm_a, alarm_d should be removed
|
||||||
|
test_vals = {'status': 'OK',
|
||||||
|
'service': 'alarm_a'}
|
||||||
|
test_vals.update(_NAGIOS_TEST_INFO)
|
||||||
|
generator = mock_driver.simple_nagios_alarm_generators(1, 1, test_vals)
|
||||||
|
alarm_a_ok = mock_driver.generate_random_events_list(generator)[0]
|
||||||
|
|
||||||
|
host_v = self.get_host_after_event(event_queue, alarm_a_ok,
|
||||||
|
processor, _TARGET_HOST)
|
||||||
|
alarms = self._get_alarms_on_host(host_v, entity_graph)
|
||||||
|
self.assertEqual(1, len(alarms))
|
||||||
|
|
||||||
|
query = {VProps.CATEGORY: EntityCategory.ALARM,
|
||||||
|
VProps.IS_DELETED: True}
|
||||||
|
deleted_alarms = entity_graph.neighbors(host_v.vertex_id,
|
||||||
|
vertex_attr_filter=query)
|
||||||
|
self.assertEqual(num_orig_vertices + len(deleted_alarms) + 1,
|
||||||
|
entity_graph.num_vertices())
|
||||||
|
|
||||||
|
query = {VProps.IS_DELETED: True}
|
||||||
|
deleted_edges = entity_graph.neighbors(host_v.vertex_id,
|
||||||
|
edge_attr_filter=query)
|
||||||
|
self.assertEqual(num_orig_edges + len(deleted_edges) + 1,
|
||||||
|
entity_graph.num_edges())
|
||||||
|
|
||||||
def get_host_after_event(self, event_queue, nagios_event,
|
def get_host_after_event(self, event_queue, nagios_event,
|
||||||
processor, target_host):
|
processor, target_host):
|
||||||
processor.process_event(nagios_event)
|
processor.process_event(nagios_event)
|
||||||
|
@ -0,0 +1,53 @@
|
|||||||
|
metadata:
|
||||||
|
name: complex_and_or_operator_deduced_alarm
|
||||||
|
definitions:
|
||||||
|
entities:
|
||||||
|
- entity:
|
||||||
|
category: ALARM
|
||||||
|
type: nagios
|
||||||
|
name: alarm_a
|
||||||
|
severity: WARNING
|
||||||
|
template_id: alarm_a
|
||||||
|
- entity:
|
||||||
|
category: ALARM
|
||||||
|
type: nagios
|
||||||
|
name: alarm_b
|
||||||
|
severity: WARNING
|
||||||
|
template_id: alarm_b
|
||||||
|
- entity:
|
||||||
|
category: ALARM
|
||||||
|
type: nagios
|
||||||
|
name: alarm_c
|
||||||
|
severity: WARNING
|
||||||
|
template_id: alarm_c
|
||||||
|
- entity:
|
||||||
|
category: RESOURCE
|
||||||
|
type: nova.host
|
||||||
|
template_id: host
|
||||||
|
relationships:
|
||||||
|
- relationship:
|
||||||
|
source: alarm_a
|
||||||
|
relationship_type: on
|
||||||
|
target: host
|
||||||
|
template_id : alarm_a_on_host
|
||||||
|
- relationship:
|
||||||
|
source: alarm_b
|
||||||
|
relationship_type: on
|
||||||
|
target: host
|
||||||
|
template_id : alarm_b_on_host
|
||||||
|
- relationship:
|
||||||
|
source: alarm_c
|
||||||
|
relationship_type: on
|
||||||
|
target: host
|
||||||
|
template_id : alarm_c_on_host
|
||||||
|
scenarios:
|
||||||
|
- scenario:
|
||||||
|
condition: (alarm_a_on_host or alarm_b_on_host) and alarm_c_on_host
|
||||||
|
actions:
|
||||||
|
- action:
|
||||||
|
action_type: raise_alarm
|
||||||
|
properties:
|
||||||
|
alarm_name: alarm_d
|
||||||
|
severity: WARNING
|
||||||
|
action_target:
|
||||||
|
target: host
|
Loading…
x
Reference in New Issue
Block a user