evaluator - loading template files
Change-Id: I588306430296d8df5df102eed257a41713a087e3
This commit is contained in:
parent
30ffba8565
commit
1e6bacf0fd
@ -22,3 +22,4 @@ keystonemiddleware>=2.3.0
|
||||
stevedore>=1.5.0 # Apache-2.0
|
||||
exrex>=0.9.4
|
||||
voluptuous>=0.8.8
|
||||
sympy>=0.7.6.1
|
||||
|
@ -24,3 +24,4 @@ testtools>=1.4.0
|
||||
exrex>=0.9.4
|
||||
stevedore>=1.5.0 # Apache-2.0
|
||||
voluptuous>=0.8.8
|
||||
sympy>=0.7.6.1
|
||||
|
@ -1,76 +0,0 @@
|
||||
# Copyright 2016 - 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.
|
||||
|
||||
|
||||
class Scenario(object):
|
||||
|
||||
TYPE_ENTITY = 'entity'
|
||||
TYPE_RELATE = 'relationship'
|
||||
|
||||
def __init__(self):
|
||||
pass
|
||||
|
||||
def get_condition(self):
|
||||
"""Returns the condition for this scenario.
|
||||
|
||||
Each condition should be formatted in 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
|
||||
For details: https://en.wikipedia.org/wiki/Disjunctive_normal_form
|
||||
|
||||
:return: condition
|
||||
"""
|
||||
entity = 'replace with vertex'
|
||||
|
||||
relationship = 'replace with edge'
|
||||
|
||||
mock_entity = (entity, self.TYPE_ENTITY, True)
|
||||
mock_relationship = (relationship, self.TYPE_RELATE, False)
|
||||
|
||||
# single "and" clause between entity and relationship
|
||||
return [(mock_entity, mock_relationship)]
|
||||
|
||||
def get_actions(self):
|
||||
"""Returns the action specifications for this scenario.
|
||||
|
||||
:return: list of actions to perform
|
||||
:rtype: ActionSpecs
|
||||
"""
|
||||
action_spec = ActionSpecs()
|
||||
return [action_spec]
|
||||
|
||||
|
||||
class ActionSpecs(object):
|
||||
|
||||
def get_type(self):
|
||||
return 'action type str'
|
||||
|
||||
def get_targets(self):
|
||||
"""Returns dict of template_ids to apply action on
|
||||
|
||||
:return: dict of string:template_id
|
||||
:rtype: dict
|
||||
"""
|
||||
|
||||
# e.g., for adding edge, need two ids. for alarms, will need only one.
|
||||
return {'source': 'source template_id',
|
||||
'target': 'target template_id'}
|
||||
|
||||
def get_properties(self):
|
||||
"""Returns the properties relevant to the action.
|
||||
|
||||
:return: dictionary of properties relevant to the action.
|
||||
:rtype: dict
|
||||
"""
|
||||
return {'prop_key': 'prop_val'}
|
73
vitrage/evaluator/scenario_repository.py
Normal file
73
vitrage/evaluator/scenario_repository.py
Normal file
@ -0,0 +1,73 @@
|
||||
# Copyright 2016 - 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 oslo_log import log
|
||||
|
||||
from vitrage.common import file_utils
|
||||
from vitrage.evaluator.template import Template
|
||||
from vitrage.evaluator.template_syntax_validator import syntax_validate
|
||||
|
||||
|
||||
LOG = log.getLogger(__name__)
|
||||
|
||||
|
||||
action_types = {
|
||||
'RAISE_ALARM': 'raise_alarm',
|
||||
'ADD_CAUSAL_RELATIONSHIP': 'add_causal_relationship',
|
||||
'SET_STATE': 'set_state'
|
||||
}
|
||||
|
||||
|
||||
class ScenarioRepository(object):
|
||||
|
||||
def __init__(self, conf):
|
||||
self._load_templates_files(conf)
|
||||
self.scenarios = {}
|
||||
|
||||
def add_template(self, template_definition):
|
||||
|
||||
if syntax_validate(template_definition):
|
||||
template = Template(template_definition)
|
||||
print(template)
|
||||
|
||||
def get_relevant_scenarios(self, element_before, element_now):
|
||||
"""Returns scenarios triggered by an event.
|
||||
|
||||
Returned scenarios are divided into two disjoint lists, based on the
|
||||
element state (before/now) that triggered the scenario condition.
|
||||
|
||||
Note that this should intuitively mean that the "before" scenarios will
|
||||
activate their "undo" operation, while the "now" will activate the
|
||||
"execute" operation.
|
||||
|
||||
:param element_before:
|
||||
:param element_now:
|
||||
:return:
|
||||
:rtype: dict
|
||||
"""
|
||||
|
||||
# trigger_id_before = 'template_id of trigger for before scenario'
|
||||
# trigger_id_now = 'template_id of trigger for now scenario'
|
||||
|
||||
# return {'before': [(scenario., trigger_id_before)],
|
||||
# 'now': [(scenario.Scenario, trigger_id_now)]}
|
||||
|
||||
pass
|
||||
|
||||
def _load_templates_files(self, conf):
|
||||
|
||||
templates_dir_path = conf.evaluator.templates_dir
|
||||
template_definitions = file_utils.load_yaml_files(templates_dir_path)
|
||||
|
||||
for template_definition in template_definitions:
|
||||
self.add_template(template_definition)
|
@ -11,11 +11,217 @@
|
||||
# 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 collections import namedtuple
|
||||
from oslo_log import log
|
||||
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
|
||||
from sympy import Symbol
|
||||
|
||||
from vitrage.evaluator.template_fields import TemplateFields as TFields
|
||||
from vitrage.graph import Edge
|
||||
from vitrage.graph import Vertex
|
||||
|
||||
|
||||
LOG = log.getLogger(__name__)
|
||||
|
||||
|
||||
ConditionVar = namedtuple('ConditionVar', ['element', 'type', 'positive'])
|
||||
ActionSpecs = namedtuple('ActionSpecs', ['type', 'targets', 'properties'])
|
||||
Scenario = namedtuple('Scenario', ['condition', 'actions'])
|
||||
|
||||
|
||||
TYPE_ENTITY = 'entity'
|
||||
TYPE_RELATIONSHIP = 'relationship'
|
||||
|
||||
|
||||
class Template(object):
|
||||
pass
|
||||
|
||||
def __init__(self, template_def):
|
||||
|
||||
super(Template, self).__init__()
|
||||
|
||||
self.template_name = template_def[TFields.METADATA][TFields.ID]
|
||||
|
||||
definitions = template_def[TFields.DEFINITIONS]
|
||||
self.entities = self._build_entities(definitions[TFields.ENTITIES])
|
||||
self.relationships = self._build_relationships(
|
||||
definitions[TFields.RELATIONSHIPS])
|
||||
|
||||
self.scenarios = self._build_scenarios(template_def[TFields.SCENARIOS])
|
||||
|
||||
@property
|
||||
def entities(self):
|
||||
return self._entities
|
||||
|
||||
@entities.setter
|
||||
def entities(self, entities):
|
||||
self._entities = entities
|
||||
|
||||
@property
|
||||
def relationships(self):
|
||||
return self._relationships
|
||||
|
||||
@relationships.setter
|
||||
def relationships(self, relationships):
|
||||
self._relationships = relationships
|
||||
|
||||
def _build_entities(self, entities_definitions):
|
||||
|
||||
entities = {}
|
||||
for entity_definition in entities_definitions:
|
||||
|
||||
entity_dict = entity_definition[TFields.ENTITY]
|
||||
template_id = entity_dict[TFields.TEMPLATE_ID]
|
||||
entities[template_id] = Vertex(template_id, entity_dict)
|
||||
|
||||
return entities
|
||||
|
||||
def _build_relationships(self, relationships_defs):
|
||||
|
||||
relationships = {}
|
||||
for relationship_def in relationships_defs:
|
||||
|
||||
relationship_dict = relationship_def[TFields.RELATIONSHIP]
|
||||
relationship = self._create_edge(relationship_dict)
|
||||
template_id = relationship_dict[TFields.TEMPLATE_ID]
|
||||
relationships[template_id] = relationship
|
||||
|
||||
return relationships
|
||||
|
||||
def _create_edge(self, relationship_dict):
|
||||
|
||||
return Edge(relationship_dict[TFields.SOURCE],
|
||||
relationship_dict[TFields.TARGET],
|
||||
relationship_dict[TFields.RELATIONSHIP_TYPE],
|
||||
relationship_dict)
|
||||
|
||||
def _build_scenarios(self, scenarios_defs):
|
||||
|
||||
scenarios = []
|
||||
for scenarios_def in scenarios_defs:
|
||||
|
||||
scenario_dict = scenarios_def[TFields.SCENARIO]
|
||||
condition = self._parse_condition(scenario_dict[TFields.CONDITION])
|
||||
action_specs = self._build_actions(scenario_dict[TFields.ACTIONS])
|
||||
scenarios.append(Scenario(condition, action_specs))
|
||||
|
||||
return scenarios
|
||||
|
||||
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]
|
||||
|
||||
target_def = action_dict[TFields.ACTION_TARGET]
|
||||
targets = self._extract_action_target(target_def)
|
||||
|
||||
properties = {}
|
||||
if TFields.PROPERTIES in action_dict:
|
||||
properties = action_dict[TFields.PROPERTIES]
|
||||
|
||||
actions.append(ActionSpecs(action_type, targets, properties))
|
||||
|
||||
return actions
|
||||
|
||||
def _extract_action_target(self, action_target):
|
||||
|
||||
targets = {}
|
||||
|
||||
for key, value in action_target.iteritems():
|
||||
targets[key] = self._extract_variable(value)
|
||||
|
||||
return targets
|
||||
|
||||
def _parse_condition(self, condition_str):
|
||||
"""Parse condition string into an object
|
||||
|
||||
The condition object is formatted in 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 object itself is a list of tuples. each tuple represents
|
||||
an AND expression compound ConditionElements. The list presents the
|
||||
OR expression e.g. [(condition_element1, condition_element2)]
|
||||
|
||||
:param condition_str: the string as it written in the template itself
|
||||
:return: Condition object
|
||||
"""
|
||||
|
||||
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(condition_dnf))]
|
||||
|
||||
if isinstance(condition_dnf, Symbol):
|
||||
return [(self._extract_condition_variable(condition_dnf, False))]
|
||||
|
||||
def convert_to_dnf_format(self, condition_str):
|
||||
|
||||
condition_str = condition_str.replace('and', '&')
|
||||
condition_str = condition_str.replace('or', '|')
|
||||
condition_str = condition_str.replace('not ', '~')
|
||||
|
||||
return to_dnf(condition_str)
|
||||
|
||||
def _extract_or_condition(self, or_condition):
|
||||
|
||||
variables = []
|
||||
for variable in or_condition.args:
|
||||
|
||||
if isinstance(variable, And):
|
||||
variables.append((self._extract_and_condition(variable)),)
|
||||
elif isinstance(variable, Not):
|
||||
variables.append((self._extract_not_condition(variable),))
|
||||
else:
|
||||
variables.append((self._extract_condition_variable(variable,
|
||||
False),))
|
||||
return variables
|
||||
|
||||
def _extract_and_condition(self, and_condition):
|
||||
|
||||
variables = ()
|
||||
for arg in and_condition.args:
|
||||
if isinstance(arg, Not):
|
||||
condition_var = self._extract_not_condition(arg)
|
||||
else:
|
||||
condition_var = self._extract_condition_variable(arg, False)
|
||||
variables = variables + condition_var
|
||||
|
||||
return variables
|
||||
|
||||
def _extract_not_condition(self, not_condition):
|
||||
self._extract_condition_variable(not_condition.args, True)
|
||||
|
||||
def _extract_condition_variable(self, symbol, not_):
|
||||
|
||||
template_id = symbol.__str__()
|
||||
variable = self._extract_variable(template_id)
|
||||
|
||||
if variable:
|
||||
return ConditionVar(variable[0], variable[1], not_)
|
||||
|
||||
return None
|
||||
|
||||
def _extract_variable(self, template_id):
|
||||
|
||||
if template_id in self.relationships:
|
||||
return self.relationships[template_id], TYPE_RELATIONSHIP
|
||||
|
||||
if template_id in self.entities:
|
||||
return self.entities[template_id], TYPE_ENTITY
|
||||
|
||||
LOG.error('Cannot find template_id = %s in template named: %s' %
|
||||
(template_id, self.template_name))
|
||||
return None
|
||||
|
@ -1,27 +1,35 @@
|
||||
# Copyright 2016 - Nokia
|
||||
# Copyright 2015 - 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
|
||||
# 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 oslo_log import log
|
||||
|
||||
from vitrage.common import file_utils
|
||||
|
||||
LOG = log.getLogger(__name__)
|
||||
|
||||
|
||||
def load_templates_files(conf):
|
||||
def syntax_validate(template_conf):
|
||||
pass
|
||||
|
||||
templates_dir_path = conf.evaluator.templates_dir
|
||||
template_files = file_utils.load_yaml_files(templates_dir_path)
|
||||
|
||||
for template_file in template_files:
|
||||
pass
|
||||
def validate_scenario_condition(condition_str):
|
||||
"""Validate the condition content.
|
||||
|
||||
Check:
|
||||
1. The brackets are valid
|
||||
|
||||
:param condition: the condition string
|
||||
:return: True if the condition itself is valid, otherwise returns False
|
||||
:rtype: bool
|
||||
"""
|
||||
pass
|
@ -1,40 +0,0 @@
|
||||
# Copyright 2016 - 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 vitrage.evaluator.scenario as scenario
|
||||
|
||||
|
||||
class ScenarioManager(object):
|
||||
|
||||
def get_relevant_scenarios(self, element_before, element_now):
|
||||
"""Returns scenarios triggered by an event.
|
||||
|
||||
Returned scenarios are divided into two disjoint lists, based on the
|
||||
element state (before/now) that triggered the scenario condition.
|
||||
|
||||
Note that this should intuitively mean that the "before" scenarios will
|
||||
activate their "undo" operation, while the "now" will activate the
|
||||
"execute" operation.
|
||||
|
||||
:param element_before:
|
||||
:param element_now:
|
||||
:return:
|
||||
:rtype: dict
|
||||
"""
|
||||
|
||||
trigger_id_before = 'template_id of trigger for before scenario'
|
||||
trigger_id_now = 'template_id of trigger for now scenario'
|
||||
|
||||
return {'before': [(scenario.Scenario(), trigger_id_before)],
|
||||
'now': [(scenario.Scenario(), trigger_id_now)]}
|
@ -32,7 +32,7 @@ DICT_STRUCTURE_SCHEMA_ERROR = '%s must refer to dictionary.'
|
||||
SCHEMA_CONTENT_ERROR = '%s must contain %s Fields.'
|
||||
|
||||
|
||||
def validate(template_conf):
|
||||
def syntax_validate(template_conf):
|
||||
|
||||
is_valid = validate_template_sections(template_conf)
|
||||
|
@ -39,13 +39,13 @@ scenarios:
|
||||
condition: alarm_on_host and host_contains_instance
|
||||
actions:
|
||||
- action:
|
||||
action_type: raise_alarm
|
||||
action_type: RAISE_ALARM
|
||||
properties:
|
||||
alarm_type: VM_CPU_SUBOPTIMAL_PERFORMANCE
|
||||
action_target:
|
||||
target: 4
|
||||
- action:
|
||||
action_type: set_state
|
||||
action_type: SET_STATE
|
||||
properties:
|
||||
state: SUBOPTIMAL
|
||||
action_target:
|
||||
@ -54,7 +54,7 @@ scenarios:
|
||||
condition: alarm_on_host and alarm_on_instance and host_contains_instance
|
||||
actions:
|
||||
- action:
|
||||
action_type: add_causal_relationship
|
||||
action_type: ADD_CAUSAL_RELATIONSHIP
|
||||
action_target:
|
||||
source: 1
|
||||
target: 2
|
||||
|
@ -11,10 +11,10 @@
|
||||
# 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 oslo_config import cfg
|
||||
from oslo_log import log as logging
|
||||
from vitrage.common import file_utils
|
||||
from vitrage.evaluator.scenario_repository import ScenarioRepository
|
||||
|
||||
from vitrage.tests import base
|
||||
from vitrage.tests.mocks import utils
|
||||
|
||||
@ -22,7 +22,7 @@ from vitrage.tests.mocks import utils
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class TemplateLoaderTest(base.BaseTest):
|
||||
class ScenarioRepositoryTest(base.BaseTest):
|
||||
|
||||
OPTS = [
|
||||
cfg.StrOpt('templates_dir',
|
||||
@ -30,17 +30,12 @@ class TemplateLoaderTest(base.BaseTest):
|
||||
),
|
||||
]
|
||||
|
||||
def setUp(self):
|
||||
super(TemplateLoaderTest, self).setUp()
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
|
||||
self.template_dir_path = utils.get_resources_dir() + '/templates'
|
||||
|
||||
self.conf = cfg.ConfigOpts()
|
||||
self.conf.register_opts(self.OPTS, group='evaluator')
|
||||
|
||||
self.template_yamls = file_utils.load_yaml_files(
|
||||
self.template_dir_path
|
||||
)
|
||||
cls.conf = cfg.ConfigOpts()
|
||||
cls.conf.register_opts(cls.OPTS, group='evaluator')
|
||||
|
||||
def test_template_loader(self):
|
||||
pass
|
||||
repository = ScenarioRepository(self.conf)
|
||||
print(repository)
|
@ -17,7 +17,7 @@ from oslo_log import log as logging
|
||||
|
||||
from vitrage.common import file_utils
|
||||
from vitrage.evaluator.template_fields import TemplateFields
|
||||
from vitrage.evaluator import template_validator
|
||||
from vitrage.evaluator import template_syntax_validator
|
||||
from vitrage.tests import base
|
||||
from vitrage.tests.mocks import utils
|
||||
|
||||
@ -40,37 +40,38 @@ class TemplateValidatorTest(base.BaseTest):
|
||||
return copy.deepcopy(self.first_template)
|
||||
|
||||
def test_template_validator(self):
|
||||
self.assertTrue(template_validator.validate(self.first_template))
|
||||
self.assertTrue(template_syntax_validator.syntax_validate(
|
||||
self.first_template))
|
||||
|
||||
def test_validate_template_without_metadata_section(self):
|
||||
|
||||
template = self.clone_template
|
||||
template.pop(TemplateFields.METADATA)
|
||||
self.assertFalse(template_validator.validate(template))
|
||||
self.assertFalse(template_syntax_validator.syntax_validate(template))
|
||||
|
||||
def test_validate_template_without_id_in_metadata_section(self):
|
||||
|
||||
template = self.clone_template
|
||||
template[TemplateFields.METADATA].pop(TemplateFields.ID)
|
||||
self.assertFalse(template_validator.validate(template))
|
||||
self.assertFalse(template_syntax_validator.syntax_validate(template))
|
||||
|
||||
def test_validate_template_without_definitions_section(self):
|
||||
|
||||
template = self.clone_template
|
||||
template.pop(TemplateFields.DEFINITIONS)
|
||||
self.assertFalse(template_validator.validate(template))
|
||||
self.assertFalse(template_syntax_validator.syntax_validate(template))
|
||||
|
||||
def test_validate_template_without_entities(self):
|
||||
|
||||
template = self.clone_template
|
||||
template[TemplateFields.DEFINITIONS].pop(TemplateFields.ENTITIES)
|
||||
self.assertFalse(template_validator.validate(template))
|
||||
self.assertFalse(template_syntax_validator.syntax_validate(template))
|
||||
|
||||
def test_validate_template_with_empty_entities(self):
|
||||
|
||||
template = self.clone_template
|
||||
template[TemplateFields.DEFINITIONS][TemplateFields.ENTITIES] = []
|
||||
self.assertFalse(template_validator.validate(template))
|
||||
self.assertFalse(template_syntax_validator.syntax_validate(template))
|
||||
|
||||
def test_validate_entity_without_required_fields(self):
|
||||
|
||||
@ -78,13 +79,13 @@ class TemplateValidatorTest(base.BaseTest):
|
||||
definitions = template[TemplateFields.DEFINITIONS]
|
||||
entity = definitions[TemplateFields.ENTITIES][0]
|
||||
entity[TemplateFields.ENTITY].pop(TemplateFields.CATEGORY)
|
||||
self.assertFalse(template_validator.validate(template))
|
||||
self.assertFalse(template_syntax_validator.syntax_validate(template))
|
||||
|
||||
template = self.clone_template
|
||||
definitions = template[TemplateFields.DEFINITIONS]
|
||||
entity = definitions[TemplateFields.ENTITIES][0]
|
||||
entity[TemplateFields.ENTITY].pop(TemplateFields.TEMPLATE_ID)
|
||||
self.assertFalse(template_validator.validate(template))
|
||||
self.assertFalse(template_syntax_validator.syntax_validate(template))
|
||||
|
||||
def test_validate_relationships_without_required_fields(self):
|
||||
|
||||
@ -92,13 +93,13 @@ class TemplateValidatorTest(base.BaseTest):
|
||||
definitions = template[TemplateFields.DEFINITIONS]
|
||||
relationship = definitions[TemplateFields.RELATIONSHIPS][0]
|
||||
relationship[TemplateFields.RELATIONSHIP].pop(TemplateFields.SOURCE)
|
||||
self.assertFalse(template_validator.validate(template))
|
||||
self.assertFalse(template_syntax_validator.syntax_validate(template))
|
||||
|
||||
template = self.clone_template
|
||||
definitions = template[TemplateFields.DEFINITIONS]
|
||||
relationship = definitions[TemplateFields.RELATIONSHIPS][0]
|
||||
relationship[TemplateFields.RELATIONSHIP].pop(TemplateFields.TARGET)
|
||||
self.assertFalse(template_validator.validate(template))
|
||||
self.assertFalse(template_syntax_validator.syntax_validate(template))
|
||||
|
||||
template = self.clone_template
|
||||
definitions = template[TemplateFields.DEFINITIONS]
|
||||
@ -106,37 +107,37 @@ class TemplateValidatorTest(base.BaseTest):
|
||||
relationship[TemplateFields.RELATIONSHIP].pop(
|
||||
TemplateFields.TEMPLATE_ID
|
||||
)
|
||||
self.assertFalse(template_validator.validate(template))
|
||||
self.assertFalse(template_syntax_validator.syntax_validate(template))
|
||||
|
||||
def test_validate_template_without_scenarios(self):
|
||||
|
||||
template = self.clone_template
|
||||
template.pop(TemplateFields.SCENARIOS)
|
||||
self.assertFalse(template_validator.validate(template))
|
||||
self.assertFalse(template_syntax_validator.syntax_validate(template))
|
||||
|
||||
def test_validate_template_with_empty_scenarios(self):
|
||||
template = self.clone_template
|
||||
template[TemplateFields.SCENARIOS] = []
|
||||
self.assertFalse(template_validator.validate(template))
|
||||
self.assertFalse(template_syntax_validator.syntax_validate(template))
|
||||
|
||||
def test_validate_scenario_without_required_fields(self):
|
||||
|
||||
template = self.clone_template
|
||||
scenario = template[TemplateFields.SCENARIOS][0]
|
||||
scenario[TemplateFields.SCENARIO].pop(TemplateFields.CONDITION)
|
||||
self.assertFalse(template_validator.validate(template))
|
||||
self.assertFalse(template_syntax_validator.syntax_validate(template))
|
||||
|
||||
template = self.clone_template
|
||||
scenario = template[TemplateFields.SCENARIOS][0]
|
||||
scenario[TemplateFields.SCENARIO].pop(TemplateFields.ACTIONS)
|
||||
self.assertFalse(template_validator.validate(template))
|
||||
self.assertFalse(template_syntax_validator.syntax_validate(template))
|
||||
|
||||
def test_validate_template_with_empty_actions(self):
|
||||
|
||||
template = self.clone_template
|
||||
scenario = template[TemplateFields.SCENARIOS][0]
|
||||
scenario[TemplateFields.SCENARIO][TemplateFields.ACTIONS] = []
|
||||
self.assertFalse(template_validator.validate(template))
|
||||
self.assertFalse(template_syntax_validator.syntax_validate(template))
|
||||
|
||||
def test_validate_action_without_required_fields(self):
|
||||
|
||||
@ -144,10 +145,10 @@ class TemplateValidatorTest(base.BaseTest):
|
||||
scenario = template[TemplateFields.SCENARIOS][0]
|
||||
action = scenario[TemplateFields.SCENARIO][TemplateFields.ACTIONS][0]
|
||||
action[TemplateFields.ACTION].pop(TemplateFields.ACTION_TYPE)
|
||||
self.assertFalse(template_validator.validate(template))
|
||||
self.assertFalse(template_syntax_validator.syntax_validate(template))
|
||||
|
||||
template = self.clone_template
|
||||
scenario = template[TemplateFields.SCENARIOS][0]
|
||||
action = scenario[TemplateFields.SCENARIO][TemplateFields.ACTIONS][0]
|
||||
action[TemplateFields.ACTION].pop(TemplateFields.ACTION_TARGET)
|
||||
self.assertFalse(template_validator.validate(template))
|
||||
self.assertFalse(template_syntax_validator.syntax_validate(template))
|
||||
|
Loading…
Reference in New Issue
Block a user