Add template validations, to handle the case of actions that don't have an action_target
Two new validations are added: 1. A condition can not be only "negative". For example, instead of: "not alarm_on_instance" one should use "instance and not_alarm_on_instance" 2. There should be an entity that is common to all parts of an "or" condition. For example, this is illegal: "alarm1_on_host or alarm2_on_instance" Change-Id: I209155ade48ba740642670891c11aeef0868197c Implements: blueprint support-external-actions
This commit is contained in:
parent
b8e20918e9
commit
df289c2933
@ -104,3 +104,8 @@ The following describes all the possible status code and their messages:
|
||||
| 133 | execute_mistral action must contain workflow field in | content |
|
||||
| | properties block | |
|
||||
+------------------+---------------------------------------------------------+-------------------------------+
|
||||
| 134 | condition can not contain only 'not' clauses | content |
|
||||
+------------------+---------------------------------------------------------+-------------------------------+
|
||||
| 135 | condition must contain a common entity for all 'or' | content |
|
||||
| | clauses | |
|
||||
+------------------+---------------------------------------------------------+-------------------------------+
|
||||
|
@ -54,6 +54,8 @@ Expression can be combined using the following logical operators:
|
||||
condition to be met.
|
||||
- "or" - indicates at least one expression must be satisfied in order for the
|
||||
condition to be met (non-exclusive or).
|
||||
- "not" - indicates that the expression must not be satisfied in order for the
|
||||
condition to be met.
|
||||
- parentheses "()" - clause indicating the scope of an expression.
|
||||
|
||||
The following are examples of valid expressions, where X, Y and Z are
|
||||
@ -66,6 +68,29 @@ relationships:
|
||||
- X and not (Y or Z)
|
||||
- X and not X
|
||||
|
||||
|
||||
A few restrictions regarding the condition format:
|
||||
|
||||
* A condition can not be entirely "negative", i.e. it must have at least one
|
||||
part that does not have a "not" in front of it.
|
||||
|
||||
For example, instead of:
|
||||
not alarm_on_instance
|
||||
use:
|
||||
instance and not alarm_on_instance
|
||||
|
||||
* There must be at least one entity that is common to all "or" clauses.
|
||||
|
||||
For example, this condition is illegal:
|
||||
alarm1_on_host or alarm2_on_instance
|
||||
This condition is legal:
|
||||
alarm1_on_instance or alarm2_on_instance
|
||||
|
||||
|
||||
For more information, see the 'Calculate the action_target' section in
|
||||
`External Actions Spec <https://specs.openstack.org/openstack/vitrage-specs/specs/pike/external-actions.html>`_
|
||||
|
||||
|
||||
Template validation status codes
|
||||
--------------------------------
|
||||
|
||||
@ -379,17 +404,6 @@ This can be used along with nova notifier to call force_down for a host
|
||||
Future support & Open Issues
|
||||
============================
|
||||
|
||||
Negation
|
||||
--------
|
||||
We need to support a "not" operator, that indicates the following expression
|
||||
must not be satisfied in order for the condition to be met. "not" should apply
|
||||
to relationships, not entities. Then we could have a condition like
|
||||
|
||||
::
|
||||
|
||||
condition: host_contains_instance and not alarm_on_instance
|
||||
|
||||
|
||||
Inequality
|
||||
----------
|
||||
Consider a template that has two entities of the same category+type, say E1 and
|
||||
|
189
vitrage/evaluator/condition.py
Normal file
189
vitrage/evaluator/condition.py
Normal file
@ -0,0 +1,189 @@
|
||||
# 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 abc
|
||||
from collections import namedtuple
|
||||
from sympy.logic.boolalg import And
|
||||
from sympy.logic.boolalg import Not
|
||||
from sympy.logic.boolalg import Or
|
||||
from sympy.logic.boolalg import to_dnf as sympy_to_dfn
|
||||
from sympy import Symbol
|
||||
|
||||
|
||||
ConditionVar = namedtuple('ConditionVar', ['symbol_name', 'positive'])
|
||||
EdgeDescription = namedtuple('EdgeDescription', ['edge', 'source', 'target'])
|
||||
|
||||
|
||||
class SymbolResolver(object):
|
||||
@abc.abstractmethod
|
||||
def is_relationship(self, symbol):
|
||||
pass
|
||||
|
||||
@abc.abstractmethod
|
||||
def get_relationship_source_id(self, relationship):
|
||||
pass
|
||||
|
||||
@abc.abstractmethod
|
||||
def get_relationship_target_id(self, relationship):
|
||||
pass
|
||||
|
||||
@abc.abstractmethod
|
||||
def get_entity_id(self, entity):
|
||||
pass
|
||||
|
||||
|
||||
def get_condition_common_targets(condition,
|
||||
definitions_index,
|
||||
symbol_resolver):
|
||||
"""Return the targets that are common to all clauses of the condition.
|
||||
|
||||
Common targets include:
|
||||
* And condition - any vertex that is part of the condition can
|
||||
be a target
|
||||
* Not condition - no vertex that is part of the condition can
|
||||
be a target
|
||||
* Or condition - vertices that appear in any "positive" part (i.e. one
|
||||
that doesn't have a 'not' in front of it) of the
|
||||
Or condition
|
||||
|
||||
A complete description of all options can be found in Vitrage
|
||||
'external-actions' spec.
|
||||
|
||||
The condition format:
|
||||
[[and_var1, and_var2, ...], or_list_2, ...]
|
||||
|
||||
:return: A set of vertices that are common to all clauses of the condition
|
||||
"""
|
||||
|
||||
clauses_targets = []
|
||||
|
||||
for clause in condition:
|
||||
clause_targets = set()
|
||||
|
||||
for term in clause:
|
||||
if term.positive:
|
||||
symbol = definitions_index.get(term.symbol_name)
|
||||
if symbol and symbol_resolver.is_relationship(symbol):
|
||||
clause_targets.add(
|
||||
symbol_resolver.get_relationship_source_id(symbol))
|
||||
clause_targets.add(
|
||||
symbol_resolver.get_relationship_target_id(symbol))
|
||||
elif symbol:
|
||||
clause_targets.add(symbol_resolver.get_entity_id(symbol))
|
||||
|
||||
clauses_targets.append(clause_targets)
|
||||
|
||||
return set.intersection(*clauses_targets)
|
||||
|
||||
|
||||
def is_condition_include_positive_clause(condition):
|
||||
"""Check if a condition is positive
|
||||
|
||||
A positive condition has at least one part that is not 'not'
|
||||
|
||||
Positive conditions:
|
||||
host_contains_instance
|
||||
host and not host_contains_instance
|
||||
|
||||
Negative conditions:
|
||||
not host_contains_instance
|
||||
not host_contains_instance or not alarm_on_host
|
||||
|
||||
The condition format:
|
||||
[[and_var1, and_var2, ...], or_list_2, ...]
|
||||
|
||||
:return: True if the condition is positive
|
||||
"""
|
||||
is_positive = False
|
||||
|
||||
for clause in condition:
|
||||
for term in clause:
|
||||
if term.positive:
|
||||
is_positive = True
|
||||
|
||||
return is_positive
|
||||
|
||||
|
||||
def parse_condition(condition_str):
|
||||
"""Parse condition string into an object
|
||||
|
||||
The condition string will be converted here into DNF (Disjunctive
|
||||
Normal Form), e.g., (X and Y) or (X and Z) or (X and V and not W)
|
||||
... where X, Y, Z, V, W are either entities or relationships
|
||||
more details: https://en.wikipedia.org/wiki/Disjunctive_normal_form
|
||||
|
||||
The condition variable lists is then extracted from the DNF object.
|
||||
It is a list of lists. Each inner list represents an AND expression
|
||||
compound condition variables. The outer list presents the OR
|
||||
expression
|
||||
|
||||
[[and_var1, and_var2, ...], or_list_2, ...]
|
||||
|
||||
:param condition_str: the string as it written in the template
|
||||
:return: condition_vars_lists
|
||||
"""
|
||||
|
||||
condition_dnf = convert_to_dnf_format(condition_str)
|
||||
|
||||
if isinstance(condition_dnf, Or):
|
||||
return extract_or_condition(condition_dnf)
|
||||
|
||||
if isinstance(condition_dnf, And):
|
||||
return [extract_and_condition(condition_dnf)]
|
||||
|
||||
if isinstance(condition_dnf, Not):
|
||||
return [(extract_not_condition_var(condition_dnf))]
|
||||
|
||||
if isinstance(condition_dnf, Symbol):
|
||||
return [[(extract_condition_var(condition_dnf, True))]]
|
||||
|
||||
|
||||
def convert_to_dnf_format(condition_str):
|
||||
|
||||
condition_str = condition_str.replace(' and ', '&')
|
||||
condition_str = condition_str.replace(' or ', '|')
|
||||
condition_str = condition_str.replace(' not ', '~')
|
||||
condition_str = condition_str.replace('not ', '~')
|
||||
|
||||
return sympy_to_dfn(condition_str)
|
||||
|
||||
|
||||
def extract_or_condition(or_condition):
|
||||
|
||||
vars_ = []
|
||||
for var in or_condition.args:
|
||||
|
||||
if isinstance(var, And):
|
||||
vars_.append(extract_and_condition(var))
|
||||
else:
|
||||
is_symbol = isinstance(var, Symbol)
|
||||
vars_.append([extract_condition_var(var, is_symbol)])
|
||||
|
||||
return vars_
|
||||
|
||||
|
||||
def extract_and_condition(and_condition):
|
||||
return [extract_condition_var(arg, isinstance(arg, Symbol))
|
||||
for arg in and_condition.args]
|
||||
|
||||
|
||||
def extract_not_condition_var(not_condition):
|
||||
return [extract_condition_var(arg, False)
|
||||
for arg in not_condition.args]
|
||||
|
||||
|
||||
def extract_condition_var(symbol, positive):
|
||||
if isinstance(symbol, Not):
|
||||
return extract_not_condition_var(symbol)[0]
|
||||
return ConditionVar(symbol.name, positive)
|
@ -13,22 +13,20 @@
|
||||
# under the License.
|
||||
|
||||
from collections import namedtuple
|
||||
from sympy.logic.boolalg import And
|
||||
from sympy.logic.boolalg import Not
|
||||
from sympy.logic.boolalg import Or
|
||||
from sympy.logic.boolalg import to_dnf as sympy_to_dfn
|
||||
from sympy import Symbol
|
||||
|
||||
from vitrage.common.constants import EdgeProperties as EProps
|
||||
from vitrage.common.constants import VertexProperties as VProps
|
||||
from vitrage.common.exception import VitrageError
|
||||
from vitrage.evaluator.condition import EdgeDescription
|
||||
from vitrage.evaluator.condition import get_condition_common_targets
|
||||
from vitrage.evaluator.condition import parse_condition
|
||||
from vitrage.evaluator.condition import SymbolResolver
|
||||
from vitrage.evaluator.template_fields import TemplateFields as TFields
|
||||
from vitrage.graph.algo_driver.sub_graph_matching import NEG_CONDITION
|
||||
from vitrage.graph.driver.networkx_graph import NXGraph
|
||||
from vitrage.graph import Edge
|
||||
from vitrage.graph import Vertex
|
||||
|
||||
ConditionVar = namedtuple('ConditionVar', ['symbol_name', 'positive'])
|
||||
ActionSpecs = namedtuple('ActionSpecs', ['type', 'targets', 'properties'])
|
||||
Scenario = namedtuple('Scenario', ['id',
|
||||
'condition',
|
||||
@ -37,8 +35,6 @@ Scenario = namedtuple('Scenario', ['id',
|
||||
'entities',
|
||||
'relationships'
|
||||
])
|
||||
EdgeDescription = namedtuple('EdgeDescription', ['edge', 'source', 'target'])
|
||||
|
||||
ENTITY = 'entity'
|
||||
RELATIONSHIP = 'relationship'
|
||||
|
||||
@ -195,8 +191,8 @@ class TemplateData(object):
|
||||
self._relationships = {}
|
||||
|
||||
self.scenario_id = scenario_id
|
||||
self.condition = self._parse_condition(
|
||||
scenario_dict[TFields.CONDITION])
|
||||
self.condition = parse_condition(scenario_dict[TFields.CONDITION])
|
||||
self.valid_target = self._calculate_missing_action_target()
|
||||
self.actions = self._build_actions(scenario_dict[TFields.ACTIONS])
|
||||
self.subgraphs = TemplateData.SubGraph.from_condition(
|
||||
self.condition,
|
||||
@ -266,90 +262,21 @@ class TemplateData(object):
|
||||
target=target,
|
||||
edge=relationship.edge)
|
||||
|
||||
@staticmethod
|
||||
def _build_actions(actions_def):
|
||||
def _build_actions(self, actions_def):
|
||||
|
||||
actions = []
|
||||
for action_def in actions_def:
|
||||
|
||||
action_dict = action_def[TFields.ACTION]
|
||||
action_type = action_dict[TFields.ACTION_TYPE]
|
||||
targets = action_dict.get(TFields.ACTION_TARGET, {})
|
||||
targets = action_dict.get(TFields.ACTION_TARGET,
|
||||
self.valid_target)
|
||||
properties = action_dict.get(TFields.PROPERTIES, {})
|
||||
|
||||
actions.append(ActionSpecs(action_type, targets, properties))
|
||||
|
||||
return actions
|
||||
|
||||
def _parse_condition(self, condition_str):
|
||||
"""Parse condition string into an object
|
||||
|
||||
The condition string will be converted here into DNF (Disjunctive
|
||||
Normal Form), e.g., (X and Y) or (X and Z) or (X and V and not W)
|
||||
... where X, Y, Z, V, W are either entities or relationships
|
||||
more details: https://en.wikipedia.org/wiki/Disjunctive_normal_form
|
||||
|
||||
The condition variable lists is then extracted from the DNF object.
|
||||
It is a list of lists. Each inner list represents an AND expression
|
||||
compound condition variables. The outer list presents the OR
|
||||
expression
|
||||
|
||||
[[and_var1, and_var2, ...], or_list_2, ...]
|
||||
|
||||
:param condition_str: the string as it written in the template
|
||||
:return: condition_vars_lists
|
||||
"""
|
||||
|
||||
condition_dnf = self.convert_to_dnf_format(condition_str)
|
||||
|
||||
if isinstance(condition_dnf, Or):
|
||||
return self._extract_or_condition(condition_dnf)
|
||||
|
||||
if isinstance(condition_dnf, And):
|
||||
return [self._extract_and_condition(condition_dnf)]
|
||||
|
||||
if isinstance(condition_dnf, Not):
|
||||
return [(self._extract_not_condition_var(condition_dnf))]
|
||||
|
||||
if isinstance(condition_dnf, Symbol):
|
||||
return [[(self._extract_condition_var(condition_dnf, True))]]
|
||||
|
||||
@staticmethod
|
||||
def convert_to_dnf_format(condition_str):
|
||||
|
||||
condition_str = condition_str.replace(' and ', '&')
|
||||
condition_str = condition_str.replace(' or ', '|')
|
||||
condition_str = condition_str.replace(' not ', '~')
|
||||
condition_str = condition_str.replace('not ', '~')
|
||||
|
||||
return sympy_to_dfn(condition_str)
|
||||
|
||||
def _extract_or_condition(self, or_condition):
|
||||
|
||||
vars_ = []
|
||||
for var in or_condition.args:
|
||||
|
||||
if isinstance(var, And):
|
||||
vars_.append(self._extract_and_condition(var))
|
||||
else:
|
||||
is_symbol = isinstance(var, Symbol)
|
||||
vars_.append([self._extract_condition_var(var, is_symbol)])
|
||||
|
||||
return vars_
|
||||
|
||||
def _extract_and_condition(self, and_condition):
|
||||
return [self._extract_condition_var(arg, isinstance(arg, Symbol))
|
||||
for arg in and_condition.args]
|
||||
|
||||
def _extract_not_condition_var(self, not_condition):
|
||||
return [self._extract_condition_var(arg, False)
|
||||
for arg in not_condition.args]
|
||||
|
||||
def _extract_condition_var(self, symbol, positive):
|
||||
if isinstance(symbol, Not):
|
||||
return self._extract_not_condition_var(symbol)[0]
|
||||
return ConditionVar(symbol.name, positive)
|
||||
|
||||
def _extract_var_and_update_index(self, symbol_name):
|
||||
|
||||
if symbol_name in self._template_relationships:
|
||||
@ -365,6 +292,36 @@ class TemplateData(object):
|
||||
self._entities[symbol_name] = entity
|
||||
return entity, ENTITY
|
||||
|
||||
def _calculate_missing_action_target(self):
|
||||
"""Return a vertex that can be used as an action target.
|
||||
|
||||
External actions like execute_mistral do not have an explicit
|
||||
action target. This parameter is a must for the sub-graph matching
|
||||
algorithm. If it is missing, we would like to select an arbitrary
|
||||
target from the condition.
|
||||
|
||||
"""
|
||||
definition_index = self._template_entities.copy()
|
||||
definition_index.update(self._template_relationships)
|
||||
targets = \
|
||||
get_condition_common_targets(self.condition,
|
||||
definition_index,
|
||||
self.TemplateDataSymbolResolver())
|
||||
return {TFields.TARGET: targets.pop()} if targets else None
|
||||
|
||||
class TemplateDataSymbolResolver(SymbolResolver):
|
||||
def is_relationship(self, symbol):
|
||||
return isinstance(symbol, EdgeDescription)
|
||||
|
||||
def get_relationship_source_id(self, relationship):
|
||||
return relationship.source.vertex_id
|
||||
|
||||
def get_relationship_target_id(self, relationship):
|
||||
return relationship.target.vertex_id
|
||||
|
||||
def get_entity_id(self, entity):
|
||||
return entity.vertex_id
|
||||
|
||||
class SubGraph(object):
|
||||
@classmethod
|
||||
def from_condition(cls, condition, extract_var):
|
||||
|
@ -20,7 +20,11 @@ from six.moves import reduce
|
||||
|
||||
from vitrage.common.constants import EdgeProperties as EProps
|
||||
from vitrage.evaluator.actions.base import ActionType
|
||||
from vitrage.evaluator.template_data import TemplateData
|
||||
from vitrage.evaluator.condition import convert_to_dnf_format
|
||||
from vitrage.evaluator.condition import get_condition_common_targets
|
||||
from vitrage.evaluator.condition import is_condition_include_positive_clause
|
||||
from vitrage.evaluator.condition import parse_condition
|
||||
from vitrage.evaluator.condition import SymbolResolver
|
||||
from vitrage.evaluator.template_fields import TemplateFields
|
||||
from vitrage.evaluator.template_validation.content. \
|
||||
add_causal_relationship_validator import AddCausalRelationshipValidator
|
||||
@ -30,6 +34,8 @@ from vitrage.evaluator.template_validation.content.base import \
|
||||
get_content_fault_result
|
||||
from vitrage.evaluator.template_validation.content.base import \
|
||||
validate_template_id
|
||||
from vitrage.evaluator.template_validation.content.execute_mistral_validator \
|
||||
import ExecuteMistralValidator
|
||||
from vitrage.evaluator.template_validation.content.mark_down_validator \
|
||||
import MarkDownValidator
|
||||
from vitrage.evaluator.template_validation.content.raise_alarm_validator \
|
||||
@ -150,7 +156,7 @@ def _validate_scenarios(scenarios, definitions_index):
|
||||
|
||||
def _validate_scenario_condition(condition, definitions_index):
|
||||
try:
|
||||
dnf_result = TemplateData.ScenarioData.convert_to_dnf_format(condition)
|
||||
dnf_result = convert_to_dnf_format(condition)
|
||||
except Exception:
|
||||
LOG.error('%s status code: %s' % (status_msgs[85], 85))
|
||||
return get_content_fault_result(85)
|
||||
@ -163,11 +169,11 @@ def _validate_scenario_condition(condition, definitions_index):
|
||||
|
||||
# template id validation
|
||||
values_to_replace = ' and ', ' or ', ' not ', 'not ', '(', ')'
|
||||
condition = reduce(lambda cond, v: cond.replace(v, ' '),
|
||||
values_to_replace,
|
||||
condition)
|
||||
condition_vars = reduce(lambda cond, v: cond.replace(v, ' '),
|
||||
values_to_replace,
|
||||
condition)
|
||||
|
||||
for condition_var in condition.split(' '):
|
||||
for condition_var in condition_vars.split(' '):
|
||||
|
||||
if len(condition_var.strip()) == 0:
|
||||
continue
|
||||
@ -176,9 +182,49 @@ def _validate_scenario_condition(condition, definitions_index):
|
||||
if not result.is_valid_config:
|
||||
return result
|
||||
|
||||
# condition structure validation
|
||||
condition_structure_result = \
|
||||
validate_condition_structure(parse_condition(condition),
|
||||
definitions_index)
|
||||
if not condition_structure_result.is_valid_config:
|
||||
return condition_structure_result
|
||||
|
||||
return get_content_correct_result()
|
||||
|
||||
|
||||
def validate_condition_structure(condition_dnf, definitions_index):
|
||||
result = validate_condition_includes_positive_clause(condition_dnf)
|
||||
if not result.is_valid_config:
|
||||
return result
|
||||
|
||||
common_targets = get_condition_common_targets(condition_dnf,
|
||||
definitions_index,
|
||||
TemplateSymbolResolver())
|
||||
|
||||
return get_content_correct_result() if common_targets \
|
||||
else get_content_fault_result(135)
|
||||
|
||||
|
||||
def validate_condition_includes_positive_clause(condition):
|
||||
return get_content_correct_result() if \
|
||||
is_condition_include_positive_clause(condition) \
|
||||
else get_content_fault_result(134)
|
||||
|
||||
|
||||
class TemplateSymbolResolver(SymbolResolver):
|
||||
def is_relationship(self, symbol):
|
||||
return TemplateFields.RELATIONSHIP_TYPE in symbol
|
||||
|
||||
def get_relationship_source_id(self, relationship):
|
||||
return relationship[TemplateFields.SOURCE]
|
||||
|
||||
def get_relationship_target_id(self, relationship):
|
||||
return relationship[TemplateFields.TARGET]
|
||||
|
||||
def get_entity_id(self, entity):
|
||||
return entity[TemplateFields.TEMPLATE_ID]
|
||||
|
||||
|
||||
def _validate_not_condition(dnf_result, definitions_index):
|
||||
"""Not operator validation
|
||||
|
||||
@ -233,6 +279,7 @@ def _validate_scenario_action(action, definitions_index):
|
||||
ActionType.SET_STATE: SetStateValidator(),
|
||||
ActionType.ADD_CAUSAL_RELATIONSHIP: AddCausalRelationshipValidator(),
|
||||
ActionType.MARK_DOWN: MarkDownValidator(),
|
||||
ActionType.EXECUTE_MISTRAL: ExecuteMistralValidator(),
|
||||
}
|
||||
|
||||
if action_type not in action_validators:
|
||||
|
@ -76,6 +76,7 @@ status_msgs = {
|
||||
' \'target_action\' block.',
|
||||
132: 'add_causal_relationship action requires action_target to be ALARM',
|
||||
133: 'execute_mistral action must contain workflow field in properties '
|
||||
'block'
|
||||
|
||||
'block',
|
||||
134: 'condition can not contain only \'not\' clauses',
|
||||
135: 'condition must contain a common entity for all \'or\' clauses',
|
||||
}
|
||||
|
@ -0,0 +1,57 @@
|
||||
metadata:
|
||||
name: complex1
|
||||
definitions:
|
||||
entities:
|
||||
- entity:
|
||||
category: ALARM
|
||||
type: zabbix
|
||||
name: alarm4
|
||||
severity: WARNING
|
||||
template_id: alarm4
|
||||
- entity:
|
||||
category: ALARM
|
||||
type: zabbix
|
||||
name: alarm5
|
||||
severity: WARNING
|
||||
template_id: alarm5
|
||||
- entity:
|
||||
category: ALARM
|
||||
type: zabbix
|
||||
name: alarm6
|
||||
severity: WARNING
|
||||
template_id: alarm6
|
||||
- entity:
|
||||
category: RESOURCE
|
||||
type: nova.instance
|
||||
template_id: instance
|
||||
relationships:
|
||||
- relationship:
|
||||
source: alarm4
|
||||
relationship_type: on
|
||||
target: instance
|
||||
template_id : alarm4_on_instance
|
||||
- relationship:
|
||||
source: alarm5
|
||||
relationship_type: on
|
||||
target: instance
|
||||
template_id : alarm5_on_instance
|
||||
- relationship:
|
||||
source: alarm6
|
||||
relationship_type: on
|
||||
target: instance
|
||||
template_id : alarm6_on_instance
|
||||
scenarios:
|
||||
- scenario:
|
||||
condition: alarm4_on_instance or (alarm5_on_instance and alarm6_on_instance)
|
||||
actions:
|
||||
- action:
|
||||
action_type: raise_alarm
|
||||
properties:
|
||||
alarm_name: alarmx
|
||||
severity: WARNING
|
||||
action_target:
|
||||
target: instance
|
||||
- action:
|
||||
action_type: execute_mistral
|
||||
properties:
|
||||
workflow: wf_3
|
@ -0,0 +1,55 @@
|
||||
metadata:
|
||||
name: complex2
|
||||
definitions:
|
||||
entities:
|
||||
- entity:
|
||||
category: ALARM
|
||||
type: zabbix
|
||||
name: alarm4
|
||||
severity: WARNING
|
||||
template_id: alarm4
|
||||
- entity:
|
||||
category: ALARM
|
||||
type: zabbix
|
||||
name: alarm5
|
||||
severity: WARNING
|
||||
template_id: alarm5
|
||||
- entity:
|
||||
category: RESOURCE
|
||||
type: nova.instance
|
||||
template_id: instance
|
||||
- entity:
|
||||
category: RESOURCE
|
||||
type: nova.host
|
||||
template_id: host
|
||||
relationships:
|
||||
- relationship:
|
||||
source: alarm4
|
||||
relationship_type: on
|
||||
target: instance
|
||||
template_id : alarm4_on_instance
|
||||
- relationship:
|
||||
source: alarm5
|
||||
relationship_type: on
|
||||
target: host
|
||||
template_id : alarm5_on_host
|
||||
- relationship:
|
||||
source: alarm4
|
||||
relationship_type: on
|
||||
target: host
|
||||
template_id : alarm4_on_host
|
||||
scenarios:
|
||||
- scenario:
|
||||
condition: alarm4_on_host or (alarm4_on_instance and alarm5_on_host)
|
||||
actions:
|
||||
- action:
|
||||
action_type: raise_alarm
|
||||
properties:
|
||||
alarm_name: alarmx
|
||||
severity: WARNING
|
||||
action_target:
|
||||
target: instance
|
||||
- action:
|
||||
action_type: execute_mistral
|
||||
properties:
|
||||
workflow: wf_3
|
@ -0,0 +1,57 @@
|
||||
metadata:
|
||||
name: complex_not
|
||||
definitions:
|
||||
entities:
|
||||
- entity:
|
||||
category: ALARM
|
||||
type: zabbix
|
||||
name: alarm4
|
||||
severity: WARNING
|
||||
template_id: alarm4
|
||||
- entity:
|
||||
category: ALARM
|
||||
type: zabbix
|
||||
name: alarm5
|
||||
severity: WARNING
|
||||
template_id: alarm5
|
||||
- entity:
|
||||
category: ALARM
|
||||
type: zabbix
|
||||
name: alarm6
|
||||
severity: WARNING
|
||||
template_id: alarm6
|
||||
- entity:
|
||||
category: RESOURCE
|
||||
type: nova.instance
|
||||
template_id: instance
|
||||
relationships:
|
||||
- relationship:
|
||||
source: alarm4
|
||||
relationship_type: on
|
||||
target: instance
|
||||
template_id : alarm4_on_instance
|
||||
- relationship:
|
||||
source: alarm5
|
||||
relationship_type: on
|
||||
target: instance
|
||||
template_id : alarm5_on_instance
|
||||
- relationship:
|
||||
source: alarm6
|
||||
relationship_type: on
|
||||
target: instance
|
||||
template_id : alarm6_on_instance
|
||||
scenarios:
|
||||
- scenario:
|
||||
condition: alarm4_on_instance or (alarm5_on_instance and not alarm6_on_instance)
|
||||
actions:
|
||||
- action:
|
||||
action_type: raise_alarm
|
||||
properties:
|
||||
alarm_name: alarmx
|
||||
severity: WARNING
|
||||
action_target:
|
||||
target: instance
|
||||
- action:
|
||||
action_type: execute_mistral
|
||||
properties:
|
||||
workflow: wf_3
|
@ -0,0 +1,57 @@
|
||||
metadata:
|
||||
name: complex_not_unsupported
|
||||
definitions:
|
||||
entities:
|
||||
- entity:
|
||||
category: ALARM
|
||||
type: zabbix
|
||||
name: alarm4
|
||||
severity: WARNING
|
||||
template_id: alarm4
|
||||
- entity:
|
||||
category: ALARM
|
||||
type: zabbix
|
||||
name: alarm5
|
||||
severity: WARNING
|
||||
template_id: alarm5
|
||||
- entity:
|
||||
category: ALARM
|
||||
type: zabbix
|
||||
name: alarm6
|
||||
severity: WARNING
|
||||
template_id: alarm6
|
||||
- entity:
|
||||
category: RESOURCE
|
||||
type: nova.instance
|
||||
template_id: instance
|
||||
relationships:
|
||||
- relationship:
|
||||
source: alarm4
|
||||
relationship_type: on
|
||||
target: instance
|
||||
template_id : alarm4_on_instance
|
||||
- relationship:
|
||||
source: alarm5
|
||||
relationship_type: on
|
||||
target: instance
|
||||
template_id : alarm5_on_instance
|
||||
- relationship:
|
||||
source: alarm6
|
||||
relationship_type: on
|
||||
target: instance
|
||||
template_id : alarm6_on_instance
|
||||
scenarios:
|
||||
- scenario:
|
||||
condition: alarm4_on_instance or (not alarm5_on_instance and not alarm6_on_instance)
|
||||
actions:
|
||||
- action:
|
||||
action_type: raise_alarm
|
||||
properties:
|
||||
alarm_name: alarmx
|
||||
severity: WARNING
|
||||
action_target:
|
||||
target: instance
|
||||
- action:
|
||||
action_type: execute_mistral
|
||||
properties:
|
||||
workflow: wf_3
|
@ -0,0 +1,35 @@
|
||||
metadata:
|
||||
name: not_edge_unsupported
|
||||
definitions:
|
||||
entities:
|
||||
- entity:
|
||||
category: ALARM
|
||||
type: nagios
|
||||
name: alarm1
|
||||
severity: WARNING
|
||||
template_id: alarm1
|
||||
- entity:
|
||||
category: RESOURCE
|
||||
type: nova.instance
|
||||
template_id: instance
|
||||
relationships:
|
||||
- relationship:
|
||||
source: alarm1
|
||||
relationship_type: on
|
||||
target: instance
|
||||
template_id : alarm1_on_instance
|
||||
scenarios:
|
||||
- scenario:
|
||||
condition: not alarm1_on_instance
|
||||
actions:
|
||||
- action:
|
||||
action_type: raise_alarm
|
||||
properties:
|
||||
alarm_name: alarm_x
|
||||
severity: WARNING
|
||||
action_target:
|
||||
target: instance
|
||||
- action:
|
||||
action_type: execute_mistral
|
||||
properties:
|
||||
workflow: wf_instance
|
@ -0,0 +1,46 @@
|
||||
metadata:
|
||||
name: not_or_unsupported
|
||||
definitions:
|
||||
entities:
|
||||
- entity:
|
||||
category: ALARM
|
||||
type: nagios
|
||||
name: alarm2
|
||||
severity: WARNING
|
||||
template_id: alarm2
|
||||
- entity:
|
||||
category: ALARM
|
||||
type: nagios
|
||||
name: alarm3
|
||||
severity: WARNING
|
||||
template_id: alarm3
|
||||
- entity:
|
||||
category: RESOURCE
|
||||
type: nova.instance
|
||||
template_id: instance3
|
||||
relationships:
|
||||
- relationship:
|
||||
source: alarm2
|
||||
relationship_type: on
|
||||
target: instance3
|
||||
template_id : alarm2_on_instance3
|
||||
- relationship:
|
||||
source: alarm3
|
||||
relationship_type: on
|
||||
target: instance3
|
||||
template_id : alarm3_on_instance3
|
||||
scenarios:
|
||||
- scenario:
|
||||
condition: not alarm2_on_instance3 or not alarm3_on_instance3
|
||||
actions:
|
||||
- action:
|
||||
action_type: raise_alarm
|
||||
properties:
|
||||
alarm_name: alarmx
|
||||
severity: WARNING
|
||||
action_target:
|
||||
target: instance3
|
||||
- action:
|
||||
action_type: execute_mistral
|
||||
properties:
|
||||
workflow: wf_3
|
@ -0,0 +1,46 @@
|
||||
metadata:
|
||||
name: not_or_unsupported2
|
||||
definitions:
|
||||
entities:
|
||||
- entity:
|
||||
category: ALARM
|
||||
type: nagios
|
||||
name: alarm2
|
||||
severity: WARNING
|
||||
template_id: alarm2
|
||||
- entity:
|
||||
category: ALARM
|
||||
type: nagios
|
||||
name: alarm3
|
||||
severity: WARNING
|
||||
template_id: alarm3
|
||||
- entity:
|
||||
category: RESOURCE
|
||||
type: nova.instance
|
||||
template_id: instance3
|
||||
relationships:
|
||||
- relationship:
|
||||
source: alarm2
|
||||
relationship_type: on
|
||||
target: instance3
|
||||
template_id : alarm2_on_instance3
|
||||
- relationship:
|
||||
source: alarm3
|
||||
relationship_type: on
|
||||
target: instance3
|
||||
template_id : alarm3_on_instance3
|
||||
scenarios:
|
||||
- scenario:
|
||||
condition: alarm2_on_instance3 or not alarm3_on_instance3
|
||||
actions:
|
||||
- action:
|
||||
action_type: raise_alarm
|
||||
properties:
|
||||
alarm_name: alarmx
|
||||
severity: WARNING
|
||||
action_target:
|
||||
target: instance3
|
||||
- action:
|
||||
action_type: execute_mistral
|
||||
properties:
|
||||
workflow: wf_3
|
@ -0,0 +1,35 @@
|
||||
metadata:
|
||||
name: one_edge
|
||||
definitions:
|
||||
entities:
|
||||
- entity:
|
||||
category: ALARM
|
||||
type: nagios
|
||||
name: alarm1
|
||||
severity: WARNING
|
||||
template_id: alarm1
|
||||
- entity:
|
||||
category: RESOURCE
|
||||
type: nova.instance
|
||||
template_id: instance
|
||||
relationships:
|
||||
- relationship:
|
||||
source: alarm1
|
||||
relationship_type: on
|
||||
target: instance
|
||||
template_id : alarm1_on_instance
|
||||
scenarios:
|
||||
- scenario:
|
||||
condition: alarm1_on_instance
|
||||
actions:
|
||||
- action:
|
||||
action_type: raise_alarm
|
||||
properties:
|
||||
alarm_name: alarm_x
|
||||
severity: WARNING
|
||||
action_target:
|
||||
target: instance
|
||||
- action:
|
||||
action_type: execute_mistral
|
||||
properties:
|
||||
workflow: wf_instance
|
@ -0,0 +1,24 @@
|
||||
metadata:
|
||||
name: one_vertex
|
||||
definitions:
|
||||
entities:
|
||||
- entity:
|
||||
category: RESOURCE
|
||||
type: nova.instance
|
||||
template_id: instance2
|
||||
relationships:
|
||||
scenarios:
|
||||
- scenario:
|
||||
condition: instance2
|
||||
actions:
|
||||
- action:
|
||||
action_type: raise_alarm
|
||||
properties:
|
||||
alarm_name: alarm_x
|
||||
severity: WARNING
|
||||
action_target:
|
||||
target: instance2
|
||||
- action:
|
||||
action_type: execute_mistral
|
||||
properties:
|
||||
workflow: wf_instance
|
@ -0,0 +1,44 @@
|
||||
metadata:
|
||||
name: simple_and
|
||||
definitions:
|
||||
entities:
|
||||
- entity:
|
||||
category: ALARM
|
||||
type: nagios
|
||||
name: alarm2
|
||||
template_id: alarm2
|
||||
- entity:
|
||||
category: ALARM
|
||||
type: nagios
|
||||
name: alarm3
|
||||
template_id: alarm3
|
||||
- entity:
|
||||
category: RESOURCE
|
||||
type: nova.instance
|
||||
template_id: instance
|
||||
relationships:
|
||||
- relationship:
|
||||
source: alarm2
|
||||
relationship_type: on
|
||||
target: instance
|
||||
template_id : alarm2_on_instance
|
||||
- relationship:
|
||||
source: alarm3
|
||||
relationship_type: on
|
||||
target: instance
|
||||
template_id : alarm3_on_instance
|
||||
scenarios:
|
||||
- scenario:
|
||||
condition: alarm2_on_instance and alarm3_on_instance
|
||||
actions:
|
||||
- action:
|
||||
action_type: raise_alarm
|
||||
properties:
|
||||
alarm_name: alarmx
|
||||
severity: WARNING
|
||||
action_target:
|
||||
target: instance
|
||||
- action:
|
||||
action_type: execute_mistral
|
||||
properties:
|
||||
workflow: wf_3
|
@ -0,0 +1,53 @@
|
||||
metadata:
|
||||
name: simple_and2
|
||||
definitions:
|
||||
entities:
|
||||
- entity:
|
||||
category: ALARM
|
||||
type: nagios
|
||||
name: alarm2
|
||||
template_id: alarm2
|
||||
- entity:
|
||||
category: ALARM
|
||||
type: nagios
|
||||
name: alarm3
|
||||
template_id: alarm3
|
||||
- entity:
|
||||
category: RESOURCE
|
||||
type: nova.host
|
||||
template_id: host
|
||||
- entity:
|
||||
category: RESOURCE
|
||||
type: nova.instance
|
||||
template_id: instance
|
||||
relationships:
|
||||
- relationship:
|
||||
source: alarm2
|
||||
relationship_type: on
|
||||
target: instance
|
||||
template_id : alarm2_on_instance
|
||||
- relationship:
|
||||
source: alarm3
|
||||
relationship_type: on
|
||||
target: instance
|
||||
template_id : alarm3_on_instance
|
||||
- relationship:
|
||||
source: host
|
||||
relationship_type: on
|
||||
target: instance
|
||||
template_id : host_contains_instance
|
||||
scenarios:
|
||||
- scenario:
|
||||
condition: alarm2_on_instance and alarm3_on_instance and host_contains_instance
|
||||
actions:
|
||||
- action:
|
||||
action_type: raise_alarm
|
||||
properties:
|
||||
alarm_name: alarmx
|
||||
severity: WARNING
|
||||
action_target:
|
||||
target: instance
|
||||
- action:
|
||||
action_type: execute_mistral
|
||||
properties:
|
||||
workflow: wf_3
|
@ -0,0 +1,46 @@
|
||||
metadata:
|
||||
name: simple_or
|
||||
definitions:
|
||||
entities:
|
||||
- entity:
|
||||
category: ALARM
|
||||
type: nagios
|
||||
name: alarm2
|
||||
severity: WARNING
|
||||
template_id: alarm2
|
||||
- entity:
|
||||
category: ALARM
|
||||
type: nagios
|
||||
name: alarm3
|
||||
severity: WARNING
|
||||
template_id: alarm3
|
||||
- entity:
|
||||
category: RESOURCE
|
||||
type: nova.instance
|
||||
template_id: instance3
|
||||
relationships:
|
||||
- relationship:
|
||||
source: alarm2
|
||||
relationship_type: on
|
||||
target: instance3
|
||||
template_id : alarm2_on_instance3
|
||||
- relationship:
|
||||
source: alarm3
|
||||
relationship_type: on
|
||||
target: instance3
|
||||
template_id : alarm3_on_instance3
|
||||
scenarios:
|
||||
- scenario:
|
||||
condition: alarm2_on_instance3 or alarm3_on_instance3
|
||||
actions:
|
||||
- action:
|
||||
action_type: raise_alarm
|
||||
properties:
|
||||
alarm_name: alarmx
|
||||
severity: WARNING
|
||||
action_target:
|
||||
target: instance3
|
||||
- action:
|
||||
action_type: execute_mistral
|
||||
properties:
|
||||
workflow: wf_3
|
@ -0,0 +1,57 @@
|
||||
metadata:
|
||||
name: simple_or2
|
||||
definitions:
|
||||
entities:
|
||||
- entity:
|
||||
category: ALARM
|
||||
type: zabbix
|
||||
name: alarm4
|
||||
severity: WARNING
|
||||
template_id: alarm4
|
||||
- entity:
|
||||
category: ALARM
|
||||
type: zabbix
|
||||
name: alarm5
|
||||
severity: WARNING
|
||||
template_id: alarm5
|
||||
- entity:
|
||||
category: ALARM
|
||||
type: zabbix
|
||||
name: alarm6
|
||||
severity: WARNING
|
||||
template_id: alarm6
|
||||
- entity:
|
||||
category: RESOURCE
|
||||
type: nova.instance
|
||||
template_id: instance
|
||||
relationships:
|
||||
- relationship:
|
||||
source: alarm4
|
||||
relationship_type: on
|
||||
target: instance
|
||||
template_id : alarm4_on_instance
|
||||
- relationship:
|
||||
source: alarm5
|
||||
relationship_type: on
|
||||
target: instance
|
||||
template_id : alarm5_on_instance
|
||||
- relationship:
|
||||
source: alarm6
|
||||
relationship_type: on
|
||||
target: instance
|
||||
template_id : alarm6_on_instance
|
||||
scenarios:
|
||||
- scenario:
|
||||
condition: alarm4_on_instance or alarm5_on_instance or alarm6_on_instance
|
||||
actions:
|
||||
- action:
|
||||
action_type: raise_alarm
|
||||
properties:
|
||||
alarm_name: alarmx
|
||||
severity: WARNING
|
||||
action_target:
|
||||
target: instance
|
||||
- action:
|
||||
action_type: execute_mistral
|
||||
properties:
|
||||
workflow: wf_3
|
@ -0,0 +1,46 @@
|
||||
metadata:
|
||||
name: simple_or3
|
||||
definitions:
|
||||
entities:
|
||||
- entity:
|
||||
category: ALARM
|
||||
type: zabbix
|
||||
name: alarm7
|
||||
severity: WARNING
|
||||
template_id: alarm7
|
||||
- entity:
|
||||
category: ALARM
|
||||
type: zabbix
|
||||
name: alarm8
|
||||
severity: WARNING
|
||||
template_id: alarm8
|
||||
- entity:
|
||||
category: RESOURCE
|
||||
type: nova.instance
|
||||
template_id: instance4
|
||||
relationships:
|
||||
- relationship:
|
||||
source: alarm7
|
||||
relationship_type: on
|
||||
target: instance4
|
||||
template_id : alarm7_on_instance4
|
||||
- relationship:
|
||||
source: alarm8
|
||||
relationship_type: on
|
||||
target: instance4
|
||||
template_id : alarm8_on_instance4
|
||||
scenarios:
|
||||
- scenario:
|
||||
condition: instance4 or alarm7_on_instance4 or alarm8_on_instance4
|
||||
actions:
|
||||
- action:
|
||||
action_type: raise_alarm
|
||||
properties:
|
||||
alarm_name: alarmx
|
||||
severity: WARNING
|
||||
action_target:
|
||||
target: instance4
|
||||
- action:
|
||||
action_type: execute_mistral
|
||||
properties:
|
||||
workflow: wf_3
|
@ -0,0 +1,55 @@
|
||||
metadata:
|
||||
name: simple_or_unsupported
|
||||
definitions:
|
||||
entities:
|
||||
- entity:
|
||||
category: ALARM
|
||||
type: zabbix
|
||||
name: alarm4
|
||||
severity: WARNING
|
||||
template_id: alarm4
|
||||
- entity:
|
||||
category: ALARM
|
||||
type: zabbix
|
||||
name: alarm5
|
||||
severity: WARNING
|
||||
template_id: alarm5
|
||||
- entity:
|
||||
category: RESOURCE
|
||||
type: nova.instance
|
||||
template_id: instance1
|
||||
- entity:
|
||||
category: RESOURCE
|
||||
type: nova.instance
|
||||
template_id: instance2
|
||||
relationships:
|
||||
- relationship:
|
||||
source: alarm4
|
||||
relationship_type: on
|
||||
target: instance1
|
||||
template_id : alarm4_on_instance1
|
||||
- relationship:
|
||||
source: alarm5
|
||||
relationship_type: on
|
||||
target: instance1
|
||||
template_id : alarm5_on_instance1
|
||||
- relationship:
|
||||
source: alarm5
|
||||
relationship_type: on
|
||||
target: instance2
|
||||
template_id : alarm5_on_instance2
|
||||
scenarios:
|
||||
- scenario:
|
||||
condition: alarm4_on_instance1 or alarm5_on_instance1 or alarm5_on_instance2
|
||||
actions:
|
||||
- action:
|
||||
action_type: raise_alarm
|
||||
properties:
|
||||
alarm_name: alarmx
|
||||
severity: WARNING
|
||||
action_target:
|
||||
target: instance1
|
||||
- action:
|
||||
action_type: execute_mistral
|
||||
properties:
|
||||
workflow: wf_3
|
@ -20,7 +20,7 @@ definitions:
|
||||
template_id : alarm_on_port
|
||||
scenarios:
|
||||
- scenario:
|
||||
condition: not alarm_on_port
|
||||
condition: port and not alarm_on_port
|
||||
actions:
|
||||
- action:
|
||||
action_type: raise_alarm
|
||||
|
@ -11,20 +11,20 @@ definitions:
|
||||
- entity:
|
||||
category: RESOURCE
|
||||
type: nova.host
|
||||
template_id: resource
|
||||
template_id: host
|
||||
relationships:
|
||||
- relationship:
|
||||
source: alarm
|
||||
target: resource
|
||||
target: host
|
||||
relationship_type: on
|
||||
template_id : alarm_on_host
|
||||
scenarios:
|
||||
- scenario:
|
||||
condition: not alarm_on_host
|
||||
condition: host and not alarm_on_host
|
||||
actions:
|
||||
- action:
|
||||
action_type: set_state
|
||||
properties:
|
||||
state: SUBOPTIMAL
|
||||
action_target:
|
||||
target: resource
|
||||
target: host
|
||||
|
@ -47,7 +47,7 @@ definitions:
|
||||
template_id : alarm_on_instance
|
||||
scenarios:
|
||||
- scenario:
|
||||
condition: zone_contains_host or host_contains_instance and not host_contains_instance or not port_attached_instance
|
||||
condition: alarm_on_instance or host_contains_instance and not zone_contains_host or port_attached_instance and not zone_contains_host
|
||||
actions:
|
||||
- action:
|
||||
action_type: set_state
|
||||
|
@ -23,6 +23,9 @@ from vitrage.tests.unit.evaluator.template_validation.content.base import \
|
||||
from vitrage.utils import file as file_utils
|
||||
|
||||
|
||||
CONDITION_TEMPLATES_DIR = '%s/templates/evaluator/conditions/%s'
|
||||
|
||||
|
||||
class TemplateContentValidatorTest(ValidatorTest):
|
||||
|
||||
# noinspection PyPep8Naming
|
||||
@ -148,6 +151,72 @@ class TemplateContentValidatorTest(ValidatorTest):
|
||||
scenario_dict[TemplateFields.CONDITION] = 'resource and aaa'
|
||||
self._execute_and_assert_with_fault_result(template, 3)
|
||||
|
||||
def test_validate_scenario_target_one_edge_condition(self):
|
||||
self._execute_condition_template_with_correct_result('one_edge.yaml')
|
||||
|
||||
def test_validate_scenario_target_one_vertex_condition(self):
|
||||
self._execute_condition_template_with_correct_result('one_vertex.yaml')
|
||||
|
||||
def test_validate_scenario_target_simple_or_condition(self):
|
||||
self._execute_condition_template_with_correct_result('simple_or.yaml')
|
||||
|
||||
def test_validate_scenario_target_simple_or2_condition(self):
|
||||
self._execute_condition_template_with_correct_result('simple_or2.yaml')
|
||||
|
||||
def test_validate_scenario_target_simple_or3_condition(self):
|
||||
self._execute_condition_template_with_correct_result('simple_or3.yaml')
|
||||
|
||||
def test_validate_scenario_target_simple_or_unsupported_condition(self):
|
||||
self._execute_condition_template_with_fault_result(
|
||||
'simple_or_unsupported.yaml', 135)
|
||||
|
||||
def test_validate_scenario_target_simple_and_condition(self):
|
||||
self._execute_condition_template_with_correct_result('simple_and.yaml')
|
||||
|
||||
def test_validate_scenario_target_simple_and2_condition(self):
|
||||
self._execute_condition_template_with_correct_result(
|
||||
'simple_and2.yaml')
|
||||
|
||||
def test_validate_scenario_target_complex1_condition(self):
|
||||
self._execute_condition_template_with_correct_result('complex1.yaml')
|
||||
|
||||
def test_validate_scenario_target_complex2_condition(self):
|
||||
self._execute_condition_template_with_correct_result('complex2.yaml')
|
||||
|
||||
def test_validate_scenario_target_not_edge_unsupported_condition(self):
|
||||
self._execute_condition_template_with_fault_result(
|
||||
'not_edge_unsupported.yaml', 134)
|
||||
|
||||
def test_validate_scenario_target_not_or_unsupported__condition(self):
|
||||
self._execute_condition_template_with_fault_result(
|
||||
'not_or_unsupported.yaml', 134)
|
||||
|
||||
def test_validate_scenario_target_not_or_unsupported2_condition(self):
|
||||
self._execute_condition_template_with_fault_result(
|
||||
'not_or_unsupported2.yaml', 135)
|
||||
|
||||
def test_validate_scenario_target_complex_not_condition(self):
|
||||
self._execute_condition_template_with_correct_result(
|
||||
'complex_not.yaml')
|
||||
|
||||
def test_validate_scenario_target_complex_not_unsupported_condition(self):
|
||||
self._execute_condition_template_with_fault_result(
|
||||
'complex_not_unsupported.yaml', 135)
|
||||
|
||||
def _execute_condition_template_with_correct_result(self, template_name):
|
||||
template_path = CONDITION_TEMPLATES_DIR % (utils.get_resources_dir(),
|
||||
template_name)
|
||||
template_definition = file_utils.load_yaml_file(template_path, True)
|
||||
self._execute_and_assert_with_correct_result(template_definition)
|
||||
|
||||
def _execute_condition_template_with_fault_result(
|
||||
self, template_name, status_code):
|
||||
template_path = CONDITION_TEMPLATES_DIR % (utils.get_resources_dir(),
|
||||
template_name)
|
||||
template_definition = file_utils.load_yaml_file(template_path, True)
|
||||
self._execute_and_assert_with_fault_result(
|
||||
template_definition, status_code)
|
||||
|
||||
def _execute_and_assert_with_fault_result(self, template, status_code):
|
||||
|
||||
result = validator.content_validation(template)
|
||||
|
119
vitrage/tests/unit/evaluator/test_condition.py
Normal file
119
vitrage/tests/unit/evaluator/test_condition.py
Normal file
@ -0,0 +1,119 @@
|
||||
# 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.
|
||||
|
||||
from vitrage.evaluator.condition import EdgeDescription
|
||||
from vitrage.evaluator.condition import SymbolResolver
|
||||
from vitrage.evaluator.template_data import TemplateData
|
||||
from vitrage.evaluator.template_validation.content.template_content_validator \
|
||||
import get_condition_common_targets
|
||||
from vitrage.tests import base
|
||||
from vitrage.tests.mocks import utils
|
||||
from vitrage.utils import file as file_utils
|
||||
|
||||
|
||||
CONDITION_TEMPLATES_DIR = '%s/templates/evaluator/conditions/%s'
|
||||
|
||||
|
||||
class ConditionTest(base.BaseTest):
|
||||
|
||||
def test_validate_scenario_target_one_edge_condition(self):
|
||||
self._check_get_condition_common_targets('one_edge.yaml',
|
||||
['alarm1', 'instance'])
|
||||
|
||||
def test_validate_scenario_target_one_vertex_condition(self):
|
||||
self._check_get_condition_common_targets('one_vertex.yaml',
|
||||
['instance2'])
|
||||
|
||||
def test_validate_scenario_target_simple_or_condition(self):
|
||||
self._check_get_condition_common_targets('simple_or.yaml',
|
||||
['instance3'])
|
||||
|
||||
def test_validate_scenario_target_simple_or2_condition(self):
|
||||
self._check_get_condition_common_targets('simple_or2.yaml',
|
||||
['instance'])
|
||||
|
||||
def test_validate_scenario_target_simple_or3_condition(self):
|
||||
self._check_get_condition_common_targets('simple_or3.yaml',
|
||||
['instance4'])
|
||||
|
||||
def test_validate_scenario_target_simple_or_unsupported_condition(self):
|
||||
self._check_get_condition_common_targets('simple_or_unsupported.yaml',
|
||||
[])
|
||||
|
||||
def test_validate_scenario_target_simple_and_condition(self):
|
||||
self._check_get_condition_common_targets(
|
||||
'simple_and.yaml', ['alarm2', 'alarm3', 'instance'])
|
||||
|
||||
def test_validate_scenario_target_simple_and2_condition(self):
|
||||
self._check_get_condition_common_targets(
|
||||
'simple_and2.yaml', ['alarm2', 'alarm3', 'instance', 'host'])
|
||||
|
||||
def test_validate_scenario_target_complex1_condition(self):
|
||||
self._check_get_condition_common_targets('complex1.yaml', ['instance'])
|
||||
|
||||
def test_validate_scenario_target_complex2_condition(self):
|
||||
self._check_get_condition_common_targets('complex2.yaml',
|
||||
['alarm4', 'host'])
|
||||
|
||||
def test_validate_scenario_target_not_edge_unsupported_condition(self):
|
||||
self._check_get_condition_common_targets('not_edge_unsupported.yaml',
|
||||
[])
|
||||
|
||||
def test_validate_scenario_target_not_or_unsupported__condition(self):
|
||||
self._check_get_condition_common_targets('not_or_unsupported.yaml',
|
||||
[])
|
||||
|
||||
def test_validate_scenario_target_not_or_unsupported2_condition(self):
|
||||
self._check_get_condition_common_targets('not_or_unsupported2.yaml',
|
||||
[])
|
||||
|
||||
def test_validate_scenario_target_complex_not_condition(self):
|
||||
self._check_get_condition_common_targets('complex_not.yaml',
|
||||
['instance'])
|
||||
|
||||
def test_validate_scenario_target_complex_not_unsupported_condition(self):
|
||||
self._check_get_condition_common_targets(
|
||||
'complex_not_unsupported.yaml', [])
|
||||
|
||||
def _check_get_condition_common_targets(self,
|
||||
template_name,
|
||||
valid_targets):
|
||||
template_path = CONDITION_TEMPLATES_DIR % (utils.get_resources_dir(),
|
||||
template_name)
|
||||
template_definition = file_utils.load_yaml_file(template_path, True)
|
||||
|
||||
template_data = TemplateData(template_definition)
|
||||
definitions_index = template_data.entities.copy()
|
||||
definitions_index.update(template_data.relationships)
|
||||
|
||||
common_targets = get_condition_common_targets(
|
||||
template_data.scenarios[0].condition,
|
||||
definitions_index,
|
||||
self.ConditionSymbolResolver())
|
||||
|
||||
self.assertIsNotNone(common_targets)
|
||||
self.assertTrue(common_targets == set(valid_targets))
|
||||
|
||||
class ConditionSymbolResolver(SymbolResolver):
|
||||
def is_relationship(self, symbol):
|
||||
return isinstance(symbol, EdgeDescription)
|
||||
|
||||
def get_relationship_source_id(self, relationship):
|
||||
return relationship.source.vertex_id
|
||||
|
||||
def get_relationship_target_id(self, relationship):
|
||||
return relationship.target.vertex_id
|
||||
|
||||
def get_entity_id(self, entity):
|
||||
return entity.vertex_id
|
@ -20,9 +20,9 @@ from vitrage.datasources.nagios import NAGIOS_DATASOURCE
|
||||
from vitrage.datasources.nova.host import NOVA_HOST_DATASOURCE
|
||||
from vitrage.entity_graph.mappings.operational_resource_state import \
|
||||
OperationalResourceState
|
||||
from vitrage.evaluator.condition import ConditionVar
|
||||
from vitrage.evaluator.scenario_evaluator import ActionType
|
||||
from vitrage.evaluator.template_data import ActionSpecs
|
||||
from vitrage.evaluator.template_data import ConditionVar
|
||||
from vitrage.evaluator.template_data import EdgeDescription
|
||||
from vitrage.evaluator.template_data import Scenario
|
||||
from vitrage.evaluator.template_data import TemplateData
|
||||
|
Loading…
x
Reference in New Issue
Block a user