Support the option to include definition template files in Vitrage templates.

Change-Id: Ie8e950842235c4bdd7542452c0770fd753cfc342
Implements: blueprint definition-templates
This commit is contained in:
Niv Oppenhaim 2017-08-01 13:53:42 +00:00
parent a54916b695
commit be733eaf1a
28 changed files with 992 additions and 54 deletions

View File

@ -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,

View File

@ -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)]

View File

@ -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')
]

View File

@ -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

View File

@ -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]

View File

@ -26,6 +26,7 @@ class TemplateFields(TemplateTopologyFields):
ACTION_TYPE = 'action_type'
CATEGORY = 'category'
CONDITION = 'condition'
INCLUDES = 'includes'
SEVERITY = 'severity'
SCENARIO = 'scenario'
STATE = 'state'

View File

@ -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]

View File

@ -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',
}

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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):

View File

@ -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)

View File

@ -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)

View File

@ -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):

View File

@ -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):

View 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