Merge "Support the option to include definition template files in Vitrage templates."
This commit is contained in:
commit
fc30c1e378
@ -34,7 +34,8 @@ class TemplateApis(object):
|
||||
FAILED_MSG = 'validation failed'
|
||||
OK_MSG = 'validation OK'
|
||||
|
||||
def __init__(self, templates):
|
||||
def __init__(self, templates, def_templates={}):
|
||||
self.def_templates = def_templates
|
||||
self.templates = templates
|
||||
|
||||
def get_templates(self, ctx):
|
||||
@ -72,10 +73,10 @@ class TemplateApis(object):
|
||||
results = []
|
||||
for template in templates:
|
||||
|
||||
template_def = template[1]
|
||||
template_definition = template[1]
|
||||
path = template[0]
|
||||
|
||||
syntax_result = syntax_validation(template_def)
|
||||
syntax_result = syntax_validation(template_definition)
|
||||
if not syntax_result.is_valid_config:
|
||||
self._add_result(path,
|
||||
self.FAILED_MSG,
|
||||
@ -85,7 +86,9 @@ class TemplateApis(object):
|
||||
results)
|
||||
continue
|
||||
|
||||
content_result = content_validation(template_def)
|
||||
content_result = content_validation(
|
||||
template_definition,
|
||||
self.def_templates)
|
||||
if not content_result.is_valid_config:
|
||||
self._add_result(path,
|
||||
self.FAILED_MSG,
|
||||
|
@ -52,7 +52,9 @@ class VitrageApiHandlerService(os_service.Service):
|
||||
endpoints = [TopologyApis(self.entity_graph, self.conf),
|
||||
AlarmApis(self.entity_graph, self.conf),
|
||||
RcaApis(self.entity_graph, self.conf),
|
||||
TemplateApis(self.scenario_repo.templates),
|
||||
TemplateApis(
|
||||
self.scenario_repo.templates,
|
||||
self.scenario_repo.def_templates),
|
||||
EventApis(self.conf),
|
||||
ResourceApis(self.entity_graph, self.conf)]
|
||||
|
||||
|
@ -24,5 +24,8 @@ OPTS = [
|
||||
cfg.StrOpt('equivalences_dir',
|
||||
default='/etc/vitrage/templates/equivalences',
|
||||
help='A path for entity equivalences used by the evaluator'
|
||||
)
|
||||
),
|
||||
cfg.StrOpt('def_templates_dir',
|
||||
default='/etc/vitrage/templates/def_templates',
|
||||
help='A path for def_template templates used by the evaluator')
|
||||
]
|
||||
|
@ -23,6 +23,10 @@ from vitrage.evaluator.equivalence_repository import EquivalenceRepository
|
||||
from vitrage.evaluator.template_data import TemplateData
|
||||
from vitrage.evaluator.template_validation.content.template_content_validator \
|
||||
import content_validation
|
||||
from vitrage.evaluator.template_validation.content.template_content_validator \
|
||||
import def_template_content_validation
|
||||
from vitrage.evaluator.template_validation.template_syntax_validator import \
|
||||
def_template_syntax_validation
|
||||
from vitrage.evaluator.template_validation.template_syntax_validator import \
|
||||
syntax_validation
|
||||
from vitrage.utils import datetime as datetime_utils
|
||||
@ -31,21 +35,32 @@ from vitrage.utils import file as file_utils
|
||||
LOG = log.getLogger(__name__)
|
||||
|
||||
EdgeKeyScenario = namedtuple('EdgeKeyScenario', ['label', 'source', 'target'])
|
||||
DEF_TEMPLATES_DIR_OPT = 'def_templates_dir'
|
||||
|
||||
|
||||
class ScenarioRepository(object):
|
||||
def __init__(self, conf):
|
||||
self._templates = {}
|
||||
self._def_templates = {}
|
||||
self.entity_equivalences = EquivalenceRepository().load_files(
|
||||
conf.evaluator.equivalences_dir)
|
||||
self.relationship_scenarios = defaultdict(list)
|
||||
self.entity_scenarios = defaultdict(list)
|
||||
self._load_def_template_files(conf)
|
||||
self._load_templates_files(conf)
|
||||
|
||||
@property
|
||||
def templates(self):
|
||||
return self._templates
|
||||
|
||||
@property
|
||||
def def_templates(self):
|
||||
return self._def_templates
|
||||
|
||||
@def_templates.setter
|
||||
def def_templates(self, def_templates):
|
||||
self._def_templates = def_templates
|
||||
|
||||
@templates.setter
|
||||
def templates(self, templates):
|
||||
self._templates = templates
|
||||
@ -82,27 +97,47 @@ class ScenarioRepository(object):
|
||||
|
||||
def add_template(self, template_def):
|
||||
|
||||
current_time = datetime_utils.utcnow()
|
||||
|
||||
result = syntax_validation(template_def)
|
||||
if not result.is_valid_config:
|
||||
LOG.info('Unable to load template: %s' % result.comment)
|
||||
LOG.info('Unable to load template, syntax err: %s'
|
||||
% result.comment)
|
||||
else:
|
||||
result = content_validation(template_def)
|
||||
result = content_validation(template_def, self._def_templates)
|
||||
if not result.is_valid_config:
|
||||
LOG.info('Unable to load template: %s' % result.comment)
|
||||
LOG.info('Unable to load template, content err: %s'
|
||||
% result.comment)
|
||||
|
||||
template_uuid = uuidutils.generate_uuid()
|
||||
current_time = datetime_utils.utcnow()
|
||||
self.templates[str(template_uuid)] = Template(template_uuid,
|
||||
template_def,
|
||||
current_time,
|
||||
result)
|
||||
if result.is_valid_config:
|
||||
template_data = TemplateData(template_def)
|
||||
template_data = TemplateData(template_def, self._def_templates)
|
||||
for scenario in template_data.scenarios:
|
||||
for equivalent_scenario in self._expand_equivalence(scenario):
|
||||
self._add_scenario(equivalent_scenario)
|
||||
|
||||
def add_def_template(self, def_template):
|
||||
|
||||
result = def_template_syntax_validation(def_template)
|
||||
if not result.is_valid_config:
|
||||
LOG.info('Unable to load definition template, syntax err: %s'
|
||||
% result.comment)
|
||||
else:
|
||||
result = def_template_content_validation(def_template)
|
||||
if not result.is_valid_config:
|
||||
LOG.info('Unable to load definition template, content err: %s'
|
||||
% result.comment)
|
||||
|
||||
current_time = datetime_utils.utcnow()
|
||||
include_uuid = uuidutils.generate_uuid()
|
||||
self._def_templates[str(include_uuid)] = Template(include_uuid,
|
||||
def_template,
|
||||
current_time,
|
||||
result)
|
||||
|
||||
def _expand_equivalence(self, scenario):
|
||||
equivalent_scenarios = [scenario]
|
||||
for symbol_name, entity in scenario.entities.items():
|
||||
@ -120,7 +155,7 @@ class ScenarioRepository(object):
|
||||
scenarios_out = list(scenarios_in)
|
||||
for entity_key in entity_keys:
|
||||
for scenario in scenarios_in:
|
||||
equivalent_scenario = TemplateData.ScenarioData.\
|
||||
equivalent_scenario = TemplateData.ScenarioData. \
|
||||
build_equivalent_scenario(scenario,
|
||||
symbol_name,
|
||||
entity_key)
|
||||
@ -133,6 +168,16 @@ class ScenarioRepository(object):
|
||||
for relationship in scenario.relationships.values():
|
||||
self._add_relationship_scenario(scenario, relationship)
|
||||
|
||||
def _load_def_template_files(self, conf):
|
||||
|
||||
if DEF_TEMPLATES_DIR_OPT in conf.evaluator:
|
||||
|
||||
def_templates_dir = conf.evaluator.def_templates_dir
|
||||
def_templates = file_utils.load_yaml_files(def_templates_dir)
|
||||
|
||||
for def_template in def_templates:
|
||||
self.add_def_template(def_template)
|
||||
|
||||
def _load_templates_files(self, conf):
|
||||
|
||||
templates_dir = conf.evaluator.templates_dir
|
||||
|
@ -14,6 +14,7 @@
|
||||
|
||||
from collections import namedtuple
|
||||
|
||||
|
||||
from vitrage.common.constants import EdgeProperties as EProps
|
||||
from vitrage.common.constants import VertexProperties as VProps
|
||||
from vitrage.common.exception import VitrageError
|
||||
@ -26,6 +27,7 @@ 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
|
||||
from vitrage.utils import evaluator as evaluator_utils
|
||||
|
||||
ActionSpecs = namedtuple('ActionSpecs', ['type', 'targets', 'properties'])
|
||||
Scenario = namedtuple('Scenario', ['id',
|
||||
@ -61,18 +63,32 @@ class TemplateData(object):
|
||||
'operational_severity': VProps.VITRAGE_OPERATIONAL_SEVERITY
|
||||
}
|
||||
|
||||
def __init__(self, template_def):
|
||||
def __init__(self, template_def, def_templates={}):
|
||||
|
||||
self.name = template_def[TFields.METADATA][TFields.NAME]
|
||||
|
||||
defs = template_def[TFields.DEFINITIONS]
|
||||
self.entities = self._build_entities(defs[TFields.ENTITIES])
|
||||
|
||||
defs = {}
|
||||
if TFields.DEFINITIONS in template_def:
|
||||
defs = template_def[TFields.DEFINITIONS]
|
||||
if TFields.ENTITIES in defs:
|
||||
self.entities = self._build_entities(defs[TFields.ENTITIES])
|
||||
self.relationships = {}
|
||||
|
||||
# Add definitions from template then from definition templates.
|
||||
if TFields.INCLUDES in template_def:
|
||||
includes = template_def[TFields.INCLUDES]
|
||||
self._build_entities_from_def_templates(
|
||||
includes, def_templates, self.entities)
|
||||
|
||||
if TFields.RELATIONSHIPS in defs:
|
||||
self.relationships = self._build_relationships(
|
||||
defs[TFields.RELATIONSHIPS])
|
||||
|
||||
if TFields.INCLUDES in template_def:
|
||||
includes = template_def[TFields.INCLUDES]
|
||||
self._build_relationships_with_def_templates(includes,
|
||||
def_templates,
|
||||
self.relationships)
|
||||
|
||||
self.scenarios = self._build_scenarios(template_def[TFields.SCENARIOS])
|
||||
|
||||
@property
|
||||
@ -120,6 +136,27 @@ class TemplateData(object):
|
||||
|
||||
return entities
|
||||
|
||||
def _build_entities_from_def_templates(
|
||||
self, includes, def_templates, entities):
|
||||
|
||||
for def_template_dict in includes:
|
||||
|
||||
name = def_template_dict[TFields.NAME]
|
||||
def_template = evaluator_utils.find_def_template(
|
||||
name, def_templates)
|
||||
defs = def_template[TFields.DEFINITIONS]
|
||||
entities_defs = defs[TFields.ENTITIES]
|
||||
|
||||
for entity_def in entities_defs:
|
||||
|
||||
entity_dict = entity_def[TFields.ENTITY]
|
||||
template_id = entity_dict[TFields.TEMPLATE_ID]
|
||||
if template_id not in entities:
|
||||
|
||||
properties = self._convert_properties_with_dictionary(
|
||||
self._extract_properties(entity_dict))
|
||||
entities[template_id] = Vertex(template_id, properties)
|
||||
|
||||
def _build_relationships(self, relationships_defs):
|
||||
|
||||
relationships = {}
|
||||
@ -132,6 +169,28 @@ class TemplateData(object):
|
||||
|
||||
return relationships
|
||||
|
||||
def _build_relationships_with_def_templates(
|
||||
self, includes, def_templates, relationships):
|
||||
|
||||
for def_template_dict in includes:
|
||||
|
||||
name = def_template_dict[TFields.NAME]
|
||||
def_template = evaluator_utils.find_def_template(
|
||||
name, def_templates)
|
||||
|
||||
if TFields.RELATIONSHIPS in def_template[TFields.DEFINITIONS]:
|
||||
defs = def_template[TFields.DEFINITIONS]
|
||||
relationship_defs = defs[TFields.RELATIONSHIPS]
|
||||
|
||||
for relationship_def in relationship_defs:
|
||||
relationship_dict = relationship_def[TFields.RELATIONSHIP]
|
||||
template_id = relationship_dict[TFields.TEMPLATE_ID]
|
||||
|
||||
if template_id not in relationships:
|
||||
relationship = self._extract_relationship_info(
|
||||
relationship_dict)
|
||||
relationships[template_id] = relationship
|
||||
|
||||
def _extract_relationship_info(self, relationship_dict):
|
||||
|
||||
source_id = relationship_dict[TFields.SOURCE]
|
||||
|
@ -26,6 +26,7 @@ class TemplateFields(TemplateTopologyFields):
|
||||
ACTION_TYPE = 'action_type'
|
||||
CATEGORY = 'category'
|
||||
CONDITION = 'condition'
|
||||
INCLUDES = 'includes'
|
||||
SEVERITY = 'severity'
|
||||
SCENARIO = 'scenario'
|
||||
STATE = 'state'
|
||||
|
@ -43,17 +43,32 @@ from vitrage.evaluator.template_validation.content.raise_alarm_validator \
|
||||
from vitrage.evaluator.template_validation.content.set_state_validator \
|
||||
import SetStateValidator
|
||||
from vitrage.evaluator.template_validation.status_messages import status_msgs
|
||||
from vitrage.utils import evaluator as evaluator_utils
|
||||
|
||||
LOG = log.getLogger(__name__)
|
||||
|
||||
|
||||
def content_validation(template):
|
||||
|
||||
template_definitions = template[TemplateFields.DEFINITIONS]
|
||||
def content_validation(template, def_templates={}):
|
||||
|
||||
result = get_content_correct_result()
|
||||
entities_index = {}
|
||||
entities = template_definitions[TemplateFields.ENTITIES]
|
||||
result = _validate_entities_definition(entities, entities_index)
|
||||
template_definitions = {}
|
||||
|
||||
if TemplateFields.DEFINITIONS in template:
|
||||
template_definitions = template[TemplateFields.DEFINITIONS]
|
||||
|
||||
if TemplateFields.ENTITIES in template_definitions:
|
||||
entities = template_definitions[TemplateFields.ENTITIES]
|
||||
result = _validate_entities_definition(entities, entities_index)
|
||||
|
||||
# If there are duplicate definitions in several includes under the same
|
||||
# name, will regard the first one
|
||||
if result.is_valid_config and TemplateFields.INCLUDES in template:
|
||||
|
||||
template_includes = template[TemplateFields.INCLUDES]
|
||||
result = _validate_definitions_with_includes(template_includes,
|
||||
def_templates,
|
||||
entities_index)
|
||||
|
||||
relationships_index = {}
|
||||
|
||||
@ -64,6 +79,15 @@ def content_validation(template):
|
||||
result = _validate_relationships_definitions(relationships,
|
||||
relationships_index,
|
||||
entities_index)
|
||||
|
||||
if result.is_valid_config and TemplateFields.INCLUDES in template:
|
||||
template_includes = template[TemplateFields.INCLUDES]
|
||||
result = _validate_relationships_definitions_with_includes(
|
||||
template_includes,
|
||||
def_templates,
|
||||
entities_index,
|
||||
relationships_index)
|
||||
|
||||
if result.is_valid_config:
|
||||
scenarios = template[TemplateFields.SCENARIOS]
|
||||
definitions_index = entities_index.copy()
|
||||
@ -73,6 +97,25 @@ def content_validation(template):
|
||||
return result
|
||||
|
||||
|
||||
def def_template_content_validation(def_template):
|
||||
def_template_definitions = def_template[TemplateFields.DEFINITIONS]
|
||||
|
||||
entities_index = {}
|
||||
entities = def_template_definitions[TemplateFields.ENTITIES]
|
||||
result = _validate_entities_definition(entities, entities_index)
|
||||
|
||||
relationships_index = {}
|
||||
|
||||
if result.is_valid_config \
|
||||
and TemplateFields.RELATIONSHIPS in def_template_definitions:
|
||||
relationships = def_template_definitions[TemplateFields.RELATIONSHIPS]
|
||||
result = _validate_relationships_definitions(relationships,
|
||||
relationships_index,
|
||||
entities_index)
|
||||
|
||||
return result
|
||||
|
||||
|
||||
def _validate_entities_definition(entities, entities_index):
|
||||
|
||||
for entity in entities:
|
||||
@ -98,6 +141,49 @@ def _validate_entity_definition(entity_dict, entities_index):
|
||||
return get_content_correct_result()
|
||||
|
||||
|
||||
def _validate_definitions_with_includes(
|
||||
template_includes, def_templates, entities_index):
|
||||
|
||||
for include in template_includes:
|
||||
|
||||
name = include[TemplateFields.NAME]
|
||||
def_template = evaluator_utils.find_def_template(name, def_templates)
|
||||
|
||||
if not def_template:
|
||||
|
||||
LOG.error('%s status code: %s' % (status_msgs[142], 142))
|
||||
return get_content_fault_result(142)
|
||||
|
||||
def_template_definitions = def_template[TemplateFields.DEFINITIONS]
|
||||
def_template_entities = \
|
||||
def_template_definitions[TemplateFields.ENTITIES]
|
||||
result = _validate_include_entities_definition(
|
||||
def_template_entities, entities_index)
|
||||
|
||||
if not result.is_valid_config:
|
||||
return result
|
||||
|
||||
return get_content_correct_result()
|
||||
|
||||
|
||||
def _validate_include_entities_definition(
|
||||
def_template_entities,
|
||||
entities_index):
|
||||
|
||||
for entity in def_template_entities:
|
||||
entity_dict = entity[TemplateFields.ENTITY]
|
||||
result = _validate_entity_definition(entity_dict, entities_index)
|
||||
|
||||
if not result.is_valid_config:
|
||||
return result
|
||||
|
||||
if entity_dict[TemplateFields.TEMPLATE_ID] not in entities_index:
|
||||
id = entity_dict[TemplateFields.TEMPLATE_ID]
|
||||
entities_index[id] = entity_dict
|
||||
|
||||
return get_content_correct_result()
|
||||
|
||||
|
||||
def _validate_relationships_definitions(relationships,
|
||||
relationships_index,
|
||||
entities_index):
|
||||
@ -116,6 +202,49 @@ def _validate_relationships_definitions(relationships,
|
||||
return get_content_correct_result()
|
||||
|
||||
|
||||
def _validate_relationships_definitions_with_includes(template_includes,
|
||||
def_templates,
|
||||
entities_index,
|
||||
relationships_index):
|
||||
|
||||
for include in template_includes:
|
||||
|
||||
name = include[TemplateFields.NAME]
|
||||
def_template = evaluator_utils.find_def_template(name, def_templates)
|
||||
|
||||
if def_template:
|
||||
defs = def_template[TemplateFields.DEFINITIONS]
|
||||
relationships = defs[TemplateFields.RELATIONSHIPS]
|
||||
|
||||
for relationship in relationships:
|
||||
relationship_dict = relationship[TemplateFields.RELATIONSHIP]
|
||||
template_id = relationship_dict[TemplateFields.TEMPLATE_ID]
|
||||
if template_id not in relationships_index:
|
||||
result = _validate_def_template_relationship(
|
||||
relationship_dict,
|
||||
entities_index)
|
||||
|
||||
if not result.is_valid_config:
|
||||
return result
|
||||
|
||||
relationships_index[template_id] = relationship_dict
|
||||
|
||||
return get_content_correct_result()
|
||||
|
||||
|
||||
def _validate_def_template_relationship(relationship,
|
||||
entities_index):
|
||||
|
||||
target = relationship[TemplateFields.TARGET]
|
||||
result = validate_template_id(entities_index, target)
|
||||
|
||||
if result.is_valid_config:
|
||||
source = relationship[TemplateFields.SOURCE]
|
||||
result = validate_template_id(entities_index, source)
|
||||
|
||||
return result
|
||||
|
||||
|
||||
def _validate_relationship(relationship, relationships_index, entities_index):
|
||||
|
||||
template_id = relationship[TemplateFields.TEMPLATE_ID]
|
||||
|
@ -79,4 +79,10 @@ status_msgs = {
|
||||
'block',
|
||||
134: 'condition can not contain only \'not\' clauses',
|
||||
135: 'condition must contain a common entity for all \'or\' clauses',
|
||||
|
||||
# def_templates status messages 140-159
|
||||
140: 'At least one template must be included',
|
||||
141: 'Name field is unspecified for include',
|
||||
142: 'Trying to include a template that does not exist',
|
||||
143: 'Includable cannot have Includes or Scenarios',
|
||||
}
|
||||
|
@ -19,6 +19,7 @@ from voluptuous import All
|
||||
from voluptuous import Any
|
||||
from voluptuous import Error
|
||||
from voluptuous import Invalid
|
||||
from voluptuous import Optional
|
||||
from voluptuous import Required
|
||||
from voluptuous import Schema
|
||||
|
||||
@ -31,21 +32,24 @@ from vitrage.evaluator.template_validation.status_messages import status_msgs
|
||||
|
||||
LOG = log.getLogger(__name__)
|
||||
|
||||
|
||||
RESULT_DESCRIPTION = 'Template syntax validation'
|
||||
|
||||
|
||||
def syntax_validation(template_conf):
|
||||
|
||||
result = _validate_template_sections(template_conf)
|
||||
|
||||
if result.is_valid_config:
|
||||
metadata = template_conf[TemplateFields.METADATA]
|
||||
result = _validate_metadata_section(metadata)
|
||||
|
||||
if result.is_valid_config:
|
||||
if result.is_valid_config and TemplateFields.INCLUDES in template_conf:
|
||||
includes = template_conf[TemplateFields.INCLUDES]
|
||||
result = _validate_includes_section(includes)
|
||||
|
||||
if result.is_valid_config and TemplateFields.DEFINITIONS in template_conf:
|
||||
definitions = template_conf[TemplateFields.DEFINITIONS]
|
||||
result = _validate_definitions_section(definitions)
|
||||
has_includes = TemplateFields.INCLUDES in template_conf
|
||||
result = _validate_definitions_section(definitions, has_includes)
|
||||
|
||||
if result.is_valid_config:
|
||||
scenarios = template_conf[TemplateFields.SCENARIOS]
|
||||
@ -54,18 +58,52 @@ def syntax_validation(template_conf):
|
||||
return result
|
||||
|
||||
|
||||
def _validate_template_sections(template_conf):
|
||||
def def_template_syntax_validation(def_template_conf):
|
||||
result = _validate_def_template_template_sections(def_template_conf)
|
||||
|
||||
if TemplateFields.INCLUDES in def_template_conf or \
|
||||
TemplateFields.SCENARIOS in def_template_conf:
|
||||
LOG.error('%s status code: %s' % (status_msgs[143], 143))
|
||||
return get_fault_result(RESULT_DESCRIPTION, 143)
|
||||
|
||||
if result.is_valid_config:
|
||||
metadata = def_template_conf[TemplateFields.METADATA]
|
||||
result = _validate_metadata_section(metadata)
|
||||
|
||||
if result.is_valid_config:
|
||||
definitions = def_template_conf[TemplateFields.DEFINITIONS]
|
||||
result = _validate_definitions_section(definitions, False)
|
||||
|
||||
return result
|
||||
|
||||
|
||||
def _validate_def_template_template_sections(def_template_conf):
|
||||
schema = Schema({
|
||||
Required(TemplateFields.DEFINITIONS, msg=21): dict,
|
||||
Required(TemplateFields.METADATA, msg=62): dict,
|
||||
Required(TemplateFields.SCENARIOS, msg=80): list
|
||||
})
|
||||
return _validate_dict_schema(schema, def_template_conf)
|
||||
|
||||
|
||||
def _validate_template_sections(template_conf):
|
||||
if TemplateFields.INCLUDES in template_conf:
|
||||
schema = Schema({
|
||||
Optional(TemplateFields.DEFINITIONS): dict,
|
||||
Required(TemplateFields.METADATA, msg=62): dict,
|
||||
Required(TemplateFields.SCENARIOS, msg=80): list,
|
||||
Optional(TemplateFields.INCLUDES): list
|
||||
})
|
||||
else:
|
||||
schema = Schema({
|
||||
Required(TemplateFields.DEFINITIONS, msg=21): dict,
|
||||
Required(TemplateFields.METADATA, msg=62): dict,
|
||||
Required(TemplateFields.SCENARIOS, msg=80): list,
|
||||
Optional(TemplateFields.INCLUDES): list
|
||||
})
|
||||
return _validate_dict_schema(schema, template_conf)
|
||||
|
||||
|
||||
def _validate_metadata_section(metadata):
|
||||
|
||||
any_str = Any(str, six.text_type)
|
||||
|
||||
schema = Schema({
|
||||
@ -75,10 +113,45 @@ def _validate_metadata_section(metadata):
|
||||
return _validate_dict_schema(schema, metadata)
|
||||
|
||||
|
||||
def _validate_definitions_section(definitions):
|
||||
def _validate_includes_section(includes):
|
||||
any_str = Any(str, six.text_type)
|
||||
if not includes:
|
||||
LOG.error('%s status code: %s' % (status_msgs[140], 140))
|
||||
return get_fault_result(RESULT_DESCRIPTION, 140)
|
||||
|
||||
if TemplateFields.RELATIONSHIPS not in definitions or \
|
||||
definitions[TemplateFields.RELATIONSHIPS] != '':
|
||||
for name in includes:
|
||||
schema = Schema({
|
||||
Required(TemplateFields.NAME, msg=141): any_str
|
||||
})
|
||||
result = _validate_name_schema(schema, name)
|
||||
if not result.is_valid_config:
|
||||
return result
|
||||
|
||||
return result
|
||||
|
||||
|
||||
def _validate_name_schema(schema, name):
|
||||
try:
|
||||
schema(name)
|
||||
except Error as e:
|
||||
|
||||
status_code = _get_status_code(e)
|
||||
if status_code:
|
||||
msg = status_msgs[status_code]
|
||||
else:
|
||||
# General syntax error
|
||||
status_code = 4
|
||||
msg = status_msgs[4] + str(e)
|
||||
|
||||
LOG.error('%s status code: %s' % (msg, status_code))
|
||||
return get_fault_result(RESULT_DESCRIPTION, status_code, msg)
|
||||
|
||||
return get_correct_result(RESULT_DESCRIPTION)
|
||||
|
||||
|
||||
def _validate_definitions_section(definitions, has_includes):
|
||||
if TemplateFields.RELATIONSHIPS not in definitions \
|
||||
or definitions[TemplateFields.RELATIONSHIPS] != '':
|
||||
schema = Schema({
|
||||
Required(TemplateFields.ENTITIES, msg=20): list,
|
||||
TemplateFields.RELATIONSHIPS: list
|
||||
@ -88,8 +161,9 @@ def _validate_definitions_section(definitions):
|
||||
else:
|
||||
result = get_correct_result(RESULT_DESCRIPTION)
|
||||
|
||||
if result.is_valid_config:
|
||||
result = _validate_entities(definitions[TemplateFields.ENTITIES])
|
||||
if result.is_valid_config and TemplateFields.ENTITIES in definitions:
|
||||
entities = definitions[TemplateFields.ENTITIES]
|
||||
result = _validate_entities(entities, has_includes)
|
||||
|
||||
relationships = definitions.get(TemplateFields.RELATIONSHIPS, None)
|
||||
if result.is_valid_config and relationships:
|
||||
@ -98,9 +172,8 @@ def _validate_definitions_section(definitions):
|
||||
return result
|
||||
|
||||
|
||||
def _validate_entities(entities):
|
||||
|
||||
if not entities:
|
||||
def _validate_entities(entities, has_includes):
|
||||
if not entities and not has_includes:
|
||||
LOG.error('%s status code: %s' % (status_msgs[43], 43))
|
||||
return get_fault_result(RESULT_DESCRIPTION, 43)
|
||||
|
||||
@ -121,7 +194,6 @@ def _validate_entities(entities):
|
||||
|
||||
|
||||
def _validate_entity_dict(entity_dict):
|
||||
|
||||
any_str = Any(str, six.text_type)
|
||||
schema = Schema({
|
||||
Required(TemplateFields.CATEGORY, msg=42):
|
||||
@ -135,7 +207,6 @@ def _validate_entity_dict(entity_dict):
|
||||
|
||||
|
||||
def _validate_relationships(relationships):
|
||||
|
||||
for relationship in relationships:
|
||||
|
||||
schema = Schema({
|
||||
@ -153,7 +224,6 @@ def _validate_relationships(relationships):
|
||||
|
||||
|
||||
def _validate_relationship_dict(relationship_dict):
|
||||
|
||||
any_str = Any(str, six.text_type)
|
||||
schema = Schema({
|
||||
Required(TemplateFields.SOURCE, msg=102): any_str,
|
||||
@ -166,7 +236,6 @@ def _validate_relationship_dict(relationship_dict):
|
||||
|
||||
|
||||
def _validate_scenarios_section(scenarios):
|
||||
|
||||
if not scenarios:
|
||||
LOG.error('%s status code: %s' % (status_msgs[81], 81))
|
||||
return get_fault_result(RESULT_DESCRIPTION, 81)
|
||||
@ -188,7 +257,6 @@ def _validate_scenarios_section(scenarios):
|
||||
|
||||
|
||||
def _validate_scenario(scenario):
|
||||
|
||||
any_str = Any(str, six.text_type)
|
||||
schema = Schema({
|
||||
Required(TemplateFields.CONDITION, msg=83): any_str,
|
||||
@ -203,7 +271,6 @@ def _validate_scenario(scenario):
|
||||
|
||||
|
||||
def _validate_actions_schema(actions):
|
||||
|
||||
if not actions:
|
||||
LOG.error('%s status code: %s' % (status_msgs[121], 121))
|
||||
return get_fault_result(RESULT_DESCRIPTION, 121)
|
||||
@ -225,7 +292,6 @@ def _validate_actions_schema(actions):
|
||||
|
||||
|
||||
def _validate_action_schema(action):
|
||||
|
||||
schema = Schema({
|
||||
Required(TemplateFields.ACTION_TYPE, msg=123):
|
||||
_validate_action_type_field(),
|
||||
@ -236,7 +302,6 @@ def _validate_action_schema(action):
|
||||
|
||||
|
||||
def _validate_dict_schema(schema, value):
|
||||
|
||||
try:
|
||||
schema(value)
|
||||
except Error as e:
|
||||
@ -268,6 +333,7 @@ def _validate_template_id_value(msg=None):
|
||||
return str(v)
|
||||
else:
|
||||
raise Invalid(msg or 1)
|
||||
|
||||
return f
|
||||
|
||||
|
||||
@ -277,6 +343,7 @@ def _validate_category_field(msg=None):
|
||||
return str(v)
|
||||
else:
|
||||
raise Invalid(msg or 45)
|
||||
|
||||
return f
|
||||
|
||||
|
||||
@ -286,4 +353,5 @@ def _validate_action_type_field(msg=None):
|
||||
return str(v)
|
||||
else:
|
||||
raise Invalid(msg or 120)
|
||||
|
||||
return f
|
||||
|
@ -13,6 +13,7 @@
|
||||
# under the License.
|
||||
|
||||
import codecs
|
||||
from collections import namedtuple
|
||||
import json
|
||||
from os.path import dirname
|
||||
from os import walk
|
||||
@ -47,6 +48,25 @@ def load_specs(target_filename, target_folder=None):
|
||||
return json.load(reader(infile))
|
||||
|
||||
|
||||
def get_def_templates_dict_from_list(def_temps_list):
|
||||
"""Turns a list of def_temps into a dictionary of def_temps where the keys
|
||||
|
||||
are their index in the list. Used by unit tests
|
||||
|
||||
:param def_temps_list: def_temp list to convert
|
||||
:type def_temps_list: list
|
||||
:return: a def_temps dict
|
||||
:rtype: dict
|
||||
"""
|
||||
|
||||
Template = namedtuple('Template', 'data')
|
||||
dict = {}
|
||||
for num, item in zip(range(len(def_temps_list)), def_temps_list):
|
||||
dict[num] = Template(item)
|
||||
|
||||
return dict
|
||||
|
||||
|
||||
def _get_full_path(target_filename, target_folder):
|
||||
"""Returns the full path for the given folder and filename
|
||||
|
||||
|
@ -0,0 +1,22 @@
|
||||
metadata:
|
||||
#a basic def_template file
|
||||
name: basic_def_template
|
||||
description: basic def_template for general tests
|
||||
definitions:
|
||||
entities:
|
||||
- entity:
|
||||
category: ALARM
|
||||
type: nagios
|
||||
name: host_problem
|
||||
template_id: alarm
|
||||
- entity:
|
||||
category: RESOURCE
|
||||
type: nova.host
|
||||
template_id: resource
|
||||
relationships:
|
||||
- relationship:
|
||||
source: alarm
|
||||
target: resource
|
||||
relationship_type: on
|
||||
template_id : alarm_on_host
|
||||
|
@ -0,0 +1,43 @@
|
||||
metadata:
|
||||
name: large_def_template
|
||||
description: definition template with entities and relationsihps for tests
|
||||
definitions:
|
||||
entities:
|
||||
- entity:
|
||||
category: ALARM
|
||||
type: nagios
|
||||
name: NETWORK_PROBLEM
|
||||
template_id: network_alarm
|
||||
- entity:
|
||||
category: ALARM
|
||||
type: nagios
|
||||
name: CLUSTER_PROBLEM
|
||||
template_id: cluster_alarm
|
||||
- entity:
|
||||
category: RESOURCE
|
||||
type: nova.zone
|
||||
template_id: nova_zone
|
||||
- entity:
|
||||
category: RESOURCE
|
||||
type: openstack.cluster
|
||||
template_id: openstack_cluster
|
||||
- entity:
|
||||
category: RESOURCE
|
||||
type: neutron.network
|
||||
template_id: neutron_network
|
||||
relationships:
|
||||
- relationship:
|
||||
source: openstack_cluster
|
||||
target: nova_zone
|
||||
relationship_type: contains
|
||||
template_id : cluster_contains_zone
|
||||
- relationship:
|
||||
source: neutron_network
|
||||
target: nova_zone
|
||||
relationship_type: attached
|
||||
template_id : network_attached_zone
|
||||
- relationship:
|
||||
source: network_alarm
|
||||
target: neutron_network
|
||||
relationship_type: on
|
||||
template_id : alarm_on_network
|
@ -0,0 +1,10 @@
|
||||
metadata:
|
||||
name: single_entity
|
||||
description: definition template with a single entity
|
||||
definitions:
|
||||
entities:
|
||||
- entity:
|
||||
category: RESOURCE
|
||||
type: nova.host
|
||||
template_id: resource
|
||||
|
@ -0,0 +1,22 @@
|
||||
metadata:
|
||||
name: def_template_with_include
|
||||
description: a def_template with an include section for FAILING tests
|
||||
includes:
|
||||
- name: some_def_template
|
||||
definitions:
|
||||
entities:
|
||||
- entity:
|
||||
category: ALARM
|
||||
type: nagios
|
||||
name: host_problem
|
||||
template_id: alarm_inc
|
||||
- entity:
|
||||
category: RESOURCE
|
||||
type: nova.host
|
||||
template_id: resource_inc
|
||||
relationships:
|
||||
- relationship:
|
||||
source: alarm_inc
|
||||
target: resource_inc
|
||||
relationship_type: on
|
||||
template_id : alarm_inc_on_host_inc
|
@ -0,0 +1,30 @@
|
||||
metadata:
|
||||
name: def_template_with_scenarios
|
||||
description: def_template with a scenarios section
|
||||
definitions:
|
||||
entities:
|
||||
- entity:
|
||||
category: ALARM
|
||||
type: nagios
|
||||
name: host_problem
|
||||
template_id: alarm_scen
|
||||
- entity:
|
||||
category: RESOURCE
|
||||
type: nova.host
|
||||
template_id: resource_scen
|
||||
relationships:
|
||||
- relationship:
|
||||
source: alarm_scen
|
||||
target: resource_scen
|
||||
relationship_type: on
|
||||
template_id : alarm_scen_on_host_scen
|
||||
scenarios:
|
||||
- scenario:
|
||||
condition: alarm_on_host_scen
|
||||
actions:
|
||||
- action:
|
||||
action_type: set_state
|
||||
properties:
|
||||
state: SUBOPTIMAL
|
||||
action_target:
|
||||
target: resource_scen
|
@ -0,0 +1,26 @@
|
||||
metadata:
|
||||
name: basic_template_with_include
|
||||
description: basic template for general tests
|
||||
includes:
|
||||
- name: basic_def_template
|
||||
definitions:
|
||||
entities:
|
||||
- entity:
|
||||
category: ALARM
|
||||
type: nagios
|
||||
name: host_problem
|
||||
template_id: alarm11
|
||||
- entity:
|
||||
category: RESOURCE
|
||||
type: nova.host
|
||||
template_id: resource11
|
||||
scenarios:
|
||||
- scenario:
|
||||
condition: alarm_on_host
|
||||
actions:
|
||||
- action:
|
||||
action_type: set_state
|
||||
properties:
|
||||
state: SUBOPTIMAL
|
||||
action_target:
|
||||
target: resource
|
@ -0,0 +1,32 @@
|
||||
metadata:
|
||||
name: basic_template_with_include
|
||||
description: basic template for general tests
|
||||
includes:
|
||||
- name: fake_def_template
|
||||
definitions:
|
||||
entities:
|
||||
- entity:
|
||||
category: ALARM
|
||||
type: nagios
|
||||
name: host_problem
|
||||
template_id: alarm11
|
||||
- entity:
|
||||
category: RESOURCE
|
||||
type: nova.host
|
||||
template_id: resource11
|
||||
relationships:
|
||||
- relationship:
|
||||
source: alarm11
|
||||
target: resource11
|
||||
relationship_type: on
|
||||
template_id : alarm11_on_host11
|
||||
scenarios:
|
||||
- scenario:
|
||||
condition: alarm_on_host11
|
||||
actions:
|
||||
- action:
|
||||
action_type: set_state
|
||||
properties:
|
||||
state: SUBOPTIMAL
|
||||
action_target:
|
||||
target: resource11
|
@ -0,0 +1,34 @@
|
||||
metadata:
|
||||
name: basic_template_with_two_includes
|
||||
description: A template file which includes two defintion templates
|
||||
includes:
|
||||
- name: basic_def_template
|
||||
- name: large_def_template
|
||||
definitions:
|
||||
entities:
|
||||
- entity:
|
||||
category: ALARM
|
||||
type: nagios
|
||||
name: host_problem
|
||||
template_id: alarm11
|
||||
- entity:
|
||||
category: RESOURCE
|
||||
type: nova.host
|
||||
template_id: resource11
|
||||
relationships:
|
||||
#cluster_alarm from large_def_template and resource from basic_def_template
|
||||
- relationship:
|
||||
source: cluster_alarm
|
||||
target: resource
|
||||
relationship_type: on
|
||||
template_id : alarm_on_resource
|
||||
scenarios:
|
||||
- scenario:
|
||||
condition: alarm_on_resource
|
||||
actions:
|
||||
- action:
|
||||
action_type: set_state
|
||||
properties:
|
||||
state: SUBOPTIMAL
|
||||
action_target:
|
||||
target: resource
|
@ -0,0 +1,25 @@
|
||||
metadata:
|
||||
name: template_with_empty_include
|
||||
description: template with an empty include to invoke error
|
||||
includes:
|
||||
definitions:
|
||||
entities:
|
||||
- entity:
|
||||
category: ALARM
|
||||
type: nagios
|
||||
name: host_problem
|
||||
template_id: alarm11
|
||||
- entity:
|
||||
category: RESOURCE
|
||||
type: nova.host
|
||||
template_id: resource11
|
||||
scenarios:
|
||||
- scenario:
|
||||
condition: alarm_on_host
|
||||
actions:
|
||||
- action:
|
||||
action_type: set_state
|
||||
properties:
|
||||
state: SUBOPTIMAL
|
||||
action_target:
|
||||
target: resource
|
@ -0,0 +1,15 @@
|
||||
metadata:
|
||||
name: no_definitions_only_include
|
||||
description: A template which only uses another definition template's definitions and has none of its own
|
||||
includes:
|
||||
- name: basic_def_template
|
||||
scenarios:
|
||||
- scenario:
|
||||
condition: alarm_on_host
|
||||
actions:
|
||||
- action:
|
||||
action_type: set_state
|
||||
properties:
|
||||
state: SUBOPTIMAL
|
||||
action_target:
|
||||
target: resource
|
@ -0,0 +1,36 @@
|
||||
metadata:
|
||||
name: only_using_def_template_definitions
|
||||
description: A template file which only uses the entities in the files it includes and has none of its own
|
||||
includes:
|
||||
- name: basic_def_template
|
||||
- name: large_def_template
|
||||
definitions:
|
||||
relationships:
|
||||
#cluster_alarm from large_def_template and resource from basic_def_template
|
||||
- relationship:
|
||||
source: cluster_alarm
|
||||
target: resource
|
||||
relationship_type: on
|
||||
template_id : alarm_on_resource
|
||||
scenarios:
|
||||
- scenario:
|
||||
condition: alarm_on_resource
|
||||
actions:
|
||||
- action:
|
||||
action_type: set_state
|
||||
properties:
|
||||
state: SUBOPTIMAL
|
||||
action_target:
|
||||
#from large_def_template
|
||||
target: openstack_cluster
|
||||
- scenario:
|
||||
#from large_def_template
|
||||
condition: alarm_on_network
|
||||
actions:
|
||||
- action:
|
||||
action_type: set_state
|
||||
properties:
|
||||
state: SUBOPTIMAL
|
||||
action_target:
|
||||
#from basic_def_template
|
||||
target: resource
|
@ -0,0 +1,16 @@
|
||||
metadata:
|
||||
name: basic_template_with_include
|
||||
description: basic template for general tests
|
||||
includes:
|
||||
- name: basic_def_template
|
||||
- name: single_entity
|
||||
scenarios:
|
||||
- scenario:
|
||||
condition: alarm_on_host
|
||||
actions:
|
||||
- action:
|
||||
action_type: set_state
|
||||
properties:
|
||||
state: SUBOPTIMAL
|
||||
action_target:
|
||||
target: resource
|
@ -33,11 +33,66 @@ class TemplateContentValidatorTest(ValidatorTest):
|
||||
def setUpClass(cls):
|
||||
|
||||
template_dir_path = '%s/templates/general' % utils.get_resources_dir()
|
||||
cls.def_templates_tests_path = '%s/templates/def_template_tests/' % \
|
||||
utils.get_resources_dir()
|
||||
cls.def_templates_dir_path = cls.def_templates_tests_path +\
|
||||
'definition_templates'
|
||||
cls.templates = file_utils.load_yaml_files(template_dir_path)
|
||||
def_templates_list = file_utils.load_yaml_files(
|
||||
cls.def_templates_dir_path)
|
||||
cls.def_templates = utils.get_def_templates_dict_from_list(
|
||||
def_templates_list)
|
||||
cls.first_template = cls.templates[0]
|
||||
|
||||
cls._hide_useless_logging_messages()
|
||||
|
||||
def test_template_with_conflicting_include_entities(self):
|
||||
template_path = '/templates/with_conflicting_include_entities.yaml'
|
||||
template = file_utils.load_yaml_file(self.def_templates_tests_path +
|
||||
template_path)
|
||||
|
||||
self._execute_and_assert_with_fault_result(template,
|
||||
2,
|
||||
self.def_templates)
|
||||
|
||||
def test_template_with_no_defs_only_includes(self):
|
||||
template_path = '/templates/only_using_def_template_definitions.yaml'
|
||||
template = file_utils.load_yaml_file(self.def_templates_tests_path +
|
||||
template_path)
|
||||
|
||||
self._execute_and_assert_with_correct_result(template,
|
||||
self.def_templates)
|
||||
|
||||
def test_template_with_multiple_includes(self):
|
||||
template_path = '/templates/basic_with_two_includes.yaml'
|
||||
template = file_utils.load_yaml_file(self.def_templates_tests_path +
|
||||
template_path)
|
||||
|
||||
self._execute_and_assert_with_correct_result(template,
|
||||
self.def_templates)
|
||||
|
||||
def test_template_with_nonexisiting_includes(self):
|
||||
|
||||
template_path = '/templates/basic_with_include_that_doesnt_exist.yaml'
|
||||
template = file_utils.load_yaml_file(self.def_templates_tests_path +
|
||||
template_path)
|
||||
self._execute_and_assert_with_fault_result(template, 142)
|
||||
|
||||
def test_template_with_missing_def_template_dir(self):
|
||||
|
||||
template_path = '/templates/basic_with_include.yaml'
|
||||
template = file_utils.load_yaml_file(self.def_templates_tests_path +
|
||||
template_path)
|
||||
self._execute_and_assert_with_fault_result(template, 142)
|
||||
|
||||
def test_template_with_include(self):
|
||||
|
||||
template_path = '/templates/basic_with_include.yaml'
|
||||
template = file_utils.load_yaml_file(self.def_templates_tests_path +
|
||||
template_path)
|
||||
self._execute_and_assert_with_correct_result(template,
|
||||
self.def_templates)
|
||||
|
||||
@property
|
||||
def clone_template(self):
|
||||
return copy.deepcopy(self.first_template)
|
||||
@ -217,14 +272,17 @@ class TemplateContentValidatorTest(ValidatorTest):
|
||||
self._execute_and_assert_with_fault_result(
|
||||
template_definition, status_code)
|
||||
|
||||
def _execute_and_assert_with_fault_result(self, template, status_code):
|
||||
def _execute_and_assert_with_fault_result(self,
|
||||
template,
|
||||
status_code,
|
||||
def_temps={}):
|
||||
|
||||
result = validator.content_validation(template)
|
||||
result = validator.content_validation(template, def_temps)
|
||||
self._assert_fault_result(result, status_code)
|
||||
|
||||
def _execute_and_assert_with_correct_result(self, template):
|
||||
def _execute_and_assert_with_correct_result(self, template, def_temps={}):
|
||||
|
||||
result = validator.content_validation(template)
|
||||
result = validator.content_validation(template, def_temps)
|
||||
self._assert_correct_result(result)
|
||||
|
||||
def _create_scenario_actions(self, target, source):
|
||||
|
@ -0,0 +1,74 @@
|
||||
# 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.template_validation.status_messages import status_msgs
|
||||
from vitrage.evaluator.template_validation import template_syntax_validator
|
||||
from vitrage.tests import base
|
||||
from vitrage.tests.mocks import utils
|
||||
from vitrage.utils import file as file_utils
|
||||
|
||||
|
||||
class DefTemplateSyntaxValidatorTest(base.BaseTest):
|
||||
|
||||
# noinspection PyPep8Naming
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
|
||||
cls.def_template_dir_path = utils.get_resources_dir() + \
|
||||
'/templates/def_template_tests'
|
||||
|
||||
def test_def_template_with_include_section(self):
|
||||
|
||||
def_template_path = self.def_template_dir_path + \
|
||||
'/definition_templates/with_include.yaml'
|
||||
def_template = file_utils.load_yaml_file(def_template_path)
|
||||
self._test_execution_with_fault_result_for_def_template(def_template,
|
||||
143)
|
||||
|
||||
def test_def_template_with_scenario_section(self):
|
||||
|
||||
def_template_path = self.def_template_dir_path + \
|
||||
'/definition_templates/with_scenarios.yaml'
|
||||
def_template = file_utils.load_yaml_file(def_template_path)
|
||||
self._test_execution_with_fault_result_for_def_template(def_template,
|
||||
143)
|
||||
|
||||
def test_basic_def_template(self):
|
||||
template_path = self.def_template_dir_path +\
|
||||
'/templates/basic_with_include.yaml'
|
||||
template = file_utils.load_yaml_file(template_path)
|
||||
|
||||
self._test_execution_with_correct_result(template)
|
||||
|
||||
def _test_execution_with_fault_result_for_def_template(self,
|
||||
def_template,
|
||||
expected_code):
|
||||
|
||||
result = template_syntax_validator.def_template_syntax_validation(
|
||||
def_template)
|
||||
|
||||
# Test assertions
|
||||
self.assertFalse(result.is_valid_config)
|
||||
self.assertTrue(result.comment.startswith(status_msgs[expected_code]))
|
||||
self.assertEqual(expected_code, result.status_code)
|
||||
|
||||
def _test_execution_with_correct_result(self, template):
|
||||
|
||||
# Test action
|
||||
result = template_syntax_validator.syntax_validation(template)
|
||||
|
||||
# Test assertions
|
||||
self.assertTrue(result.is_valid_config)
|
||||
self.assertEqual(result.comment, status_msgs[0])
|
||||
self.assertEqual(0, result.status_code)
|
@ -29,6 +29,8 @@ class TemplateSyntaxValidatorTest(base.BaseTest):
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
|
||||
cls.def_template_dir_path = utils.get_resources_dir() + \
|
||||
'/templates/def_template_tests'
|
||||
template_dir_path = '%s/templates/general' % utils.get_resources_dir()
|
||||
cls.template_yamls = file_utils.load_yaml_files(template_dir_path)
|
||||
cls.first_template = cls.template_yamls[0]
|
||||
@ -189,6 +191,19 @@ class TemplateSyntaxValidatorTest(base.BaseTest):
|
||||
scenario[TemplateFields.SCENARIO][TemplateFields.ACTIONS] = []
|
||||
self._test_execution_with_fault_result(template, 121)
|
||||
|
||||
def test_template_with_include_with_empty_name(self):
|
||||
template_path = self.def_template_dir_path +\
|
||||
'/templates/include_with_empty_name.yaml'
|
||||
template = file_utils.load_yaml_file(template_path)
|
||||
self._test_execution_with_fault_result(template, 4)
|
||||
|
||||
def test_template_with_include_with_no_defnitions(self):
|
||||
template_path = self.def_template_dir_path +\
|
||||
'/templates/no_definitions_only_include.yaml'
|
||||
template = file_utils.load_yaml_file(template_path)
|
||||
|
||||
self._test_execution_with_correct_result(template)
|
||||
|
||||
def _test_validate_action_without_required_fields(self):
|
||||
|
||||
self._test_validate_action_without_required_field(
|
||||
@ -214,7 +229,9 @@ class TemplateSyntaxValidatorTest(base.BaseTest):
|
||||
|
||||
self._test_execution_with_fault_result(template, status_msgs[100])
|
||||
|
||||
def _test_execution_with_fault_result(self, template, expected_code):
|
||||
def _test_execution_with_fault_result(self,
|
||||
template,
|
||||
expected_code):
|
||||
|
||||
# Test action
|
||||
result = template_syntax_validator.syntax_validation(template)
|
||||
|
@ -28,7 +28,6 @@ from vitrage.utils import file as file_utils
|
||||
|
||||
class ScenarioRepositoryTest(base.BaseTest):
|
||||
BASE_DIR = utils.get_resources_dir() + '/templates/general'
|
||||
HOST_HIGH_CPU = 'host_high_cpu_load_to_instance_cpu_suboptimal'
|
||||
OPTS = [
|
||||
cfg.StrOpt('templates_dir',
|
||||
default=BASE_DIR,
|
||||
@ -103,9 +102,15 @@ class EquivalentScenarioTest(base.BaseTest):
|
||||
BASE_DIR = utils.get_resources_dir() + '/templates/equivalent_scenarios/'
|
||||
OPTS = [
|
||||
cfg.StrOpt('templates_dir',
|
||||
default=BASE_DIR),
|
||||
default=BASE_DIR,
|
||||
),
|
||||
cfg.StrOpt('def_templates_dir',
|
||||
default=(utils.get_resources_dir() +
|
||||
'/templates/def_template_tests'),
|
||||
),
|
||||
cfg.StrOpt('equivalences_dir',
|
||||
default=BASE_DIR + '/equivalences')]
|
||||
default=BASE_DIR + '/equivalences',),
|
||||
]
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
|
@ -37,6 +37,119 @@ from vitrage.utils import file as file_utils
|
||||
class BasicTemplateTest(base.BaseTest):
|
||||
|
||||
BASIC_TEMPLATE = 'basic.yaml'
|
||||
BASIC_TEMPLATE_WITH_INCLUDE = 'basic_with_include.yaml'
|
||||
DEF_TEMPLATE_TESTS_DIR = utils.get_resources_dir() +\
|
||||
'/templates/def_template_tests'
|
||||
|
||||
def test_basic_template_with_include(self):
|
||||
|
||||
# Test setup
|
||||
template_path = self.DEF_TEMPLATE_TESTS_DIR +\
|
||||
'/templates/%s' % self.BASIC_TEMPLATE_WITH_INCLUDE
|
||||
template_definition = file_utils.load_yaml_file(template_path, True)
|
||||
def_templates_path = self.DEF_TEMPLATE_TESTS_DIR + \
|
||||
'/definition_templates'
|
||||
def_demplates_list = file_utils.load_yaml_files(
|
||||
def_templates_path)
|
||||
def_templates_dict = utils.get_def_templates_dict_from_list(
|
||||
def_demplates_list)
|
||||
template_data = TemplateData(template_definition, def_templates_dict)
|
||||
entities = template_data.entities
|
||||
relationships = template_data.relationships
|
||||
scenarios = template_data.scenarios
|
||||
definitions = template_definition[TFields.DEFINITIONS]
|
||||
def_template = file_utils.load_yaml_file(
|
||||
def_templates_path + '/basic_def_template.yaml')
|
||||
def_template_entities = \
|
||||
def_template[TFields.DEFINITIONS][TFields.ENTITIES]
|
||||
def_template_relationships = \
|
||||
def_template[TFields.DEFINITIONS][TFields.RELATIONSHIPS]
|
||||
definitions[TFields.ENTITIES] += def_template_entities
|
||||
definitions[TFields.RELATIONSHIPS] = def_template_relationships
|
||||
|
||||
# Assertions
|
||||
for definition in definitions[TFields.ENTITIES]:
|
||||
for key, value in definition['entity'].items():
|
||||
new_key = TemplateData.PROPS_CONVERSION[key] if key in \
|
||||
TemplateData.PROPS_CONVERSION else key
|
||||
del definition['entity'][key]
|
||||
definition['entity'][new_key] = value
|
||||
self._validate_entities(entities, definitions[TFields.ENTITIES])
|
||||
|
||||
relate_def = def_template_relationships
|
||||
self._validate_relationships(relationships, relate_def, entities)
|
||||
self._validate_scenarios(scenarios, entities)
|
||||
|
||||
expected_entities = {
|
||||
'alarm11': Vertex(
|
||||
vertex_id='alarm11',
|
||||
properties={VProps.VITRAGE_CATEGORY: EntityCategory.ALARM,
|
||||
VProps.VITRAGE_TYPE: NAGIOS_DATASOURCE,
|
||||
VProps.NAME: 'host_problem'
|
||||
}),
|
||||
'resource11': Vertex(
|
||||
vertex_id='resource11',
|
||||
properties={VProps.VITRAGE_CATEGORY: EntityCategory.RESOURCE,
|
||||
VProps.VITRAGE_TYPE: NOVA_HOST_DATASOURCE
|
||||
}),
|
||||
'alarm': Vertex(
|
||||
vertex_id='alarm',
|
||||
properties={VProps.VITRAGE_CATEGORY: EntityCategory.ALARM,
|
||||
VProps.VITRAGE_TYPE: NAGIOS_DATASOURCE,
|
||||
VProps.NAME: 'host_problem'
|
||||
}),
|
||||
'resource': Vertex(
|
||||
vertex_id='resource',
|
||||
properties={VProps.VITRAGE_CATEGORY: EntityCategory.RESOURCE,
|
||||
VProps.VITRAGE_TYPE: NOVA_HOST_DATASOURCE
|
||||
})
|
||||
}
|
||||
expected_relationships = {
|
||||
'alarm_on_host': EdgeDescription(
|
||||
edge=Edge(source_id='alarm',
|
||||
target_id='resource',
|
||||
label=EdgeLabel.ON,
|
||||
properties={EdgeProperties.RELATIONSHIP_TYPE:
|
||||
EdgeLabel.ON}),
|
||||
source=expected_entities['alarm'],
|
||||
target=expected_entities['resource']
|
||||
),
|
||||
}
|
||||
|
||||
scenario_entities = {
|
||||
'alarm': Vertex(
|
||||
vertex_id='alarm',
|
||||
properties={VProps.VITRAGE_CATEGORY: EntityCategory.ALARM,
|
||||
VProps.VITRAGE_TYPE: NAGIOS_DATASOURCE,
|
||||
VProps.NAME: 'host_problem'
|
||||
}),
|
||||
'resource': Vertex(
|
||||
vertex_id='resource',
|
||||
properties={VProps.VITRAGE_CATEGORY: EntityCategory.RESOURCE,
|
||||
VProps.VITRAGE_TYPE: NOVA_HOST_DATASOURCE
|
||||
})
|
||||
}
|
||||
|
||||
expected_scenario = Scenario(
|
||||
id='basic_template_with_include-scenario0',
|
||||
condition=[
|
||||
[ConditionVar(symbol_name='alarm_on_host',
|
||||
positive=True)]],
|
||||
actions=[
|
||||
ActionSpecs(
|
||||
type=ActionType.SET_STATE,
|
||||
targets={'target': 'resource'},
|
||||
properties={'state':
|
||||
OperationalResourceState.SUBOPTIMAL})],
|
||||
subgraphs=template_data.scenarios[0].subgraphs,
|
||||
entities=scenario_entities,
|
||||
relationships=expected_relationships
|
||||
)
|
||||
|
||||
self._validate_strict_equal(template_data,
|
||||
expected_entities,
|
||||
expected_relationships,
|
||||
expected_scenario)
|
||||
|
||||
def test_basic_template(self):
|
||||
|
||||
|
24
vitrage/utils/evaluator.py
Normal file
24
vitrage/utils/evaluator.py
Normal file
@ -0,0 +1,24 @@
|
||||
# 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.template_fields import TemplateFields
|
||||
|
||||
|
||||
def find_def_template(name, def_templates):
|
||||
|
||||
for def_template_obj in def_templates.values():
|
||||
def_template = def_template_obj.data
|
||||
if def_template[TemplateFields.METADATA][TemplateFields.NAME] == name:
|
||||
|
||||
return def_template
|
||||
return None
|
Loading…
x
Reference in New Issue
Block a user