diff --git a/vitrage/api_handler/apis/template.py b/vitrage/api_handler/apis/template.py index 6359761f2..04301cc6c 100644 --- a/vitrage/api_handler/apis/template.py +++ b/vitrage/api_handler/apis/template.py @@ -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, diff --git a/vitrage/api_handler/service.py b/vitrage/api_handler/service.py index 2fd507ff9..5213f77b1 100644 --- a/vitrage/api_handler/service.py +++ b/vitrage/api_handler/service.py @@ -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)] diff --git a/vitrage/evaluator/__init__.py b/vitrage/evaluator/__init__.py index ee0c40f13..a06e7fffe 100644 --- a/vitrage/evaluator/__init__.py +++ b/vitrage/evaluator/__init__.py @@ -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') ] diff --git a/vitrage/evaluator/scenario_repository.py b/vitrage/evaluator/scenario_repository.py index 7baf130bb..051e07499 100644 --- a/vitrage/evaluator/scenario_repository.py +++ b/vitrage/evaluator/scenario_repository.py @@ -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 diff --git a/vitrage/evaluator/template_data.py b/vitrage/evaluator/template_data.py index 86a21a22f..e80e87830 100644 --- a/vitrage/evaluator/template_data.py +++ b/vitrage/evaluator/template_data.py @@ -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] diff --git a/vitrage/evaluator/template_fields.py b/vitrage/evaluator/template_fields.py index 920b794c6..3920aab78 100644 --- a/vitrage/evaluator/template_fields.py +++ b/vitrage/evaluator/template_fields.py @@ -26,6 +26,7 @@ class TemplateFields(TemplateTopologyFields): ACTION_TYPE = 'action_type' CATEGORY = 'category' CONDITION = 'condition' + INCLUDES = 'includes' SEVERITY = 'severity' SCENARIO = 'scenario' STATE = 'state' diff --git a/vitrage/evaluator/template_validation/content/template_content_validator.py b/vitrage/evaluator/template_validation/content/template_content_validator.py index 32de1c1e5..f3fe4a527 100644 --- a/vitrage/evaluator/template_validation/content/template_content_validator.py +++ b/vitrage/evaluator/template_validation/content/template_content_validator.py @@ -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] diff --git a/vitrage/evaluator/template_validation/status_messages.py b/vitrage/evaluator/template_validation/status_messages.py index 2bb57e57b..9d235ce3d 100644 --- a/vitrage/evaluator/template_validation/status_messages.py +++ b/vitrage/evaluator/template_validation/status_messages.py @@ -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', } diff --git a/vitrage/evaluator/template_validation/template_syntax_validator.py b/vitrage/evaluator/template_validation/template_syntax_validator.py index 8df45e157..1ce9ee40d 100644 --- a/vitrage/evaluator/template_validation/template_syntax_validator.py +++ b/vitrage/evaluator/template_validation/template_syntax_validator.py @@ -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 diff --git a/vitrage/tests/mocks/utils.py b/vitrage/tests/mocks/utils.py index 0e3f47789..6e2d15d25 100644 --- a/vitrage/tests/mocks/utils.py +++ b/vitrage/tests/mocks/utils.py @@ -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 diff --git a/vitrage/tests/resources/templates/def_template_tests/definition_templates/basic_def_template.yaml b/vitrage/tests/resources/templates/def_template_tests/definition_templates/basic_def_template.yaml new file mode 100644 index 000000000..b3c0ffa8a --- /dev/null +++ b/vitrage/tests/resources/templates/def_template_tests/definition_templates/basic_def_template.yaml @@ -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 + diff --git a/vitrage/tests/resources/templates/def_template_tests/definition_templates/large_def_template.yaml b/vitrage/tests/resources/templates/def_template_tests/definition_templates/large_def_template.yaml new file mode 100644 index 000000000..0508b4b72 --- /dev/null +++ b/vitrage/tests/resources/templates/def_template_tests/definition_templates/large_def_template.yaml @@ -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 \ No newline at end of file diff --git a/vitrage/tests/resources/templates/def_template_tests/definition_templates/single_entity.yaml b/vitrage/tests/resources/templates/def_template_tests/definition_templates/single_entity.yaml new file mode 100644 index 000000000..34c728a57 --- /dev/null +++ b/vitrage/tests/resources/templates/def_template_tests/definition_templates/single_entity.yaml @@ -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 + diff --git a/vitrage/tests/resources/templates/def_template_tests/definition_templates/with_include.yaml b/vitrage/tests/resources/templates/def_template_tests/definition_templates/with_include.yaml new file mode 100644 index 000000000..dc89442d2 --- /dev/null +++ b/vitrage/tests/resources/templates/def_template_tests/definition_templates/with_include.yaml @@ -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 diff --git a/vitrage/tests/resources/templates/def_template_tests/definition_templates/with_scenarios.yaml b/vitrage/tests/resources/templates/def_template_tests/definition_templates/with_scenarios.yaml new file mode 100644 index 000000000..36cff9c84 --- /dev/null +++ b/vitrage/tests/resources/templates/def_template_tests/definition_templates/with_scenarios.yaml @@ -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 \ No newline at end of file diff --git a/vitrage/tests/resources/templates/def_template_tests/templates/basic_with_include.yaml b/vitrage/tests/resources/templates/def_template_tests/templates/basic_with_include.yaml new file mode 100644 index 000000000..a490e5951 --- /dev/null +++ b/vitrage/tests/resources/templates/def_template_tests/templates/basic_with_include.yaml @@ -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 diff --git a/vitrage/tests/resources/templates/def_template_tests/templates/basic_with_include_that_doesnt_exist.yaml b/vitrage/tests/resources/templates/def_template_tests/templates/basic_with_include_that_doesnt_exist.yaml new file mode 100644 index 000000000..108399557 --- /dev/null +++ b/vitrage/tests/resources/templates/def_template_tests/templates/basic_with_include_that_doesnt_exist.yaml @@ -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 diff --git a/vitrage/tests/resources/templates/def_template_tests/templates/basic_with_two_includes.yaml b/vitrage/tests/resources/templates/def_template_tests/templates/basic_with_two_includes.yaml new file mode 100644 index 000000000..31e9e2657 --- /dev/null +++ b/vitrage/tests/resources/templates/def_template_tests/templates/basic_with_two_includes.yaml @@ -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 diff --git a/vitrage/tests/resources/templates/def_template_tests/templates/include_with_empty_name.yaml b/vitrage/tests/resources/templates/def_template_tests/templates/include_with_empty_name.yaml new file mode 100644 index 000000000..323600881 --- /dev/null +++ b/vitrage/tests/resources/templates/def_template_tests/templates/include_with_empty_name.yaml @@ -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 \ No newline at end of file diff --git a/vitrage/tests/resources/templates/def_template_tests/templates/no_definitions_only_include.yaml b/vitrage/tests/resources/templates/def_template_tests/templates/no_definitions_only_include.yaml new file mode 100644 index 000000000..147b9428e --- /dev/null +++ b/vitrage/tests/resources/templates/def_template_tests/templates/no_definitions_only_include.yaml @@ -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 diff --git a/vitrage/tests/resources/templates/def_template_tests/templates/only_using_def_template_definitions.yaml b/vitrage/tests/resources/templates/def_template_tests/templates/only_using_def_template_definitions.yaml new file mode 100644 index 000000000..7ea9177b3 --- /dev/null +++ b/vitrage/tests/resources/templates/def_template_tests/templates/only_using_def_template_definitions.yaml @@ -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 \ No newline at end of file diff --git a/vitrage/tests/resources/templates/def_template_tests/templates/with_conflicting_include_entities.yaml b/vitrage/tests/resources/templates/def_template_tests/templates/with_conflicting_include_entities.yaml new file mode 100644 index 000000000..d1d033438 --- /dev/null +++ b/vitrage/tests/resources/templates/def_template_tests/templates/with_conflicting_include_entities.yaml @@ -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 diff --git a/vitrage/tests/unit/evaluator/template_validation/content/test_template_content_validator.py b/vitrage/tests/unit/evaluator/template_validation/content/test_template_content_validator.py index 5b1ac01dd..d10d64907 100644 --- a/vitrage/tests/unit/evaluator/template_validation/content/test_template_content_validator.py +++ b/vitrage/tests/unit/evaluator/template_validation/content/test_template_content_validator.py @@ -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): diff --git a/vitrage/tests/unit/evaluator/template_validation/test_def_template_syntax_validator.py b/vitrage/tests/unit/evaluator/template_validation/test_def_template_syntax_validator.py new file mode 100644 index 000000000..6d50f5064 --- /dev/null +++ b/vitrage/tests/unit/evaluator/template_validation/test_def_template_syntax_validator.py @@ -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) diff --git a/vitrage/tests/unit/evaluator/template_validation/test_template_syntax_validator.py b/vitrage/tests/unit/evaluator/template_validation/test_template_syntax_validator.py index 279dd476f..4db16fb44 100644 --- a/vitrage/tests/unit/evaluator/template_validation/test_template_syntax_validator.py +++ b/vitrage/tests/unit/evaluator/template_validation/test_template_syntax_validator.py @@ -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) diff --git a/vitrage/tests/unit/evaluator/test_scenario_repository.py b/vitrage/tests/unit/evaluator/test_scenario_repository.py index afd8b8a3e..906b5c6f4 100644 --- a/vitrage/tests/unit/evaluator/test_scenario_repository.py +++ b/vitrage/tests/unit/evaluator/test_scenario_repository.py @@ -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): diff --git a/vitrage/tests/unit/evaluator/test_template_data.py b/vitrage/tests/unit/evaluator/test_template_data.py index 057b40aad..0be8245f9 100644 --- a/vitrage/tests/unit/evaluator/test_template_data.py +++ b/vitrage/tests/unit/evaluator/test_template_data.py @@ -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): diff --git a/vitrage/utils/evaluator.py b/vitrage/utils/evaluator.py new file mode 100644 index 000000000..b59a0cd12 --- /dev/null +++ b/vitrage/utils/evaluator.py @@ -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