Support parameters in Vitrage templates

A new option to define parameters inside Vitrage templates, and assign
them with actual values when adding the template.

This functionality is part of template version 2, and will be added to
template version 3 in the next patch set.

Documentation will be added in the next patch set.

Story: 2004056
Task: 27062

Change-Id: Iac163aaf6de0e029893386fc4274421df4706c4e
This commit is contained in:
Ifat Afek 2019-01-28 10:00:06 +00:00
parent dd79ed9d52
commit 76fd30fd13
30 changed files with 1159 additions and 69 deletions

View File

@ -0,0 +1,5 @@
---
features:
- Added support for parameters in Vitrage templates. A template may contain
one or more ``parameters`` that are assigned with actual values upon
template creation. This enables easy reuse of common templates.

View File

@ -89,9 +89,10 @@ class TemplateController(RootRestController):
pecan.request.enforcer,
{})
template_type = kwargs['template_type']
params = kwargs.get('params')
try:
return self._add(templates, template_type)
return self._add(templates, template_type, params)
except Exception:
LOG.exception('Failed to add template.')
abort(404, 'Failed to add template.')
@ -108,12 +109,13 @@ class TemplateController(RootRestController):
templates = kwargs['templates']
template_type = kwargs.get('template_type')
params = kwargs.get('params')
try:
return self._validate(templates, template_type)
return self._validate(templates, template_type, params)
except Exception:
LOG.exception('Failed to validate template(s).')
abort(404, 'Failed to validate template.')
abort(404, 'Failed to validate template(s).')
@classmethod
def _get_templates(cls):
@ -142,12 +144,13 @@ class TemplateController(RootRestController):
abort(404, 'Failed to show template.')
@staticmethod
def _validate(templates, template_type):
def _validate(templates, template_type, params):
result_json = pecan.request.client.call(pecan.request.context,
'validate_template',
templates=templates,
template_type=template_type)
template_type=template_type,
params=params)
try:
return json.loads(result_json)
except Exception:
@ -155,13 +158,15 @@ class TemplateController(RootRestController):
abort(404, 'Failed to validate template file.')
@classmethod
def _add(cls, templates, template_type):
def _add(cls, templates, template_type, params):
try:
results = pecan.request.client.call(
pecan.request.context,
'add_template',
templates=templates,
template_type=template_type)
template_type=template_type,
params=params,
)
return results
except Exception:
LOG.exception('Failed to add template file.')

View File

@ -33,29 +33,29 @@ class TemplateApis(object):
self.notifier = notifier
self.db = db
def validate_template(self, ctx, templates, template_type):
def validate_template(self, ctx, templates, template_type, params=None):
LOG.debug("TemplateApis validate_template type: %s content: %s",
str(template_type), str(templates))
files_content = [t[1] for t in templates]
paths = [t[0] for t in templates]
results = template_repo.validate_templates(self.db, files_content,
template_type)
template_type, params)
results = [_to_result(r, p) for r, p in zip(results, paths)]
return json.dumps({'results': results})
def add_template(self, ctx, templates, template_type):
def add_template(self, ctx, templates, template_type, params=None):
"""Signal the evaluator
A new template has been added to the database with a status of
LOADING that needs to be handled.
"""
LOG.debug("TemplateApis add_template type: %s content: %s",
str(template_type), str(templates))
LOG.debug("TemplateApis add_template type: %s content: %s params: %s",
template_type, templates, params)
files_content = [t[1] for t in templates]
db_rows = template_repo.add_templates_to_db(self.db, files_content,
template_type)
template_type, params)
if self._is_evaluator_reload_required(db_rows):
LOG.info("Adding templates..")
self.notifier.notify("add template", {'template_action': 'add'})

View File

@ -12,7 +12,6 @@
# License for the specific language governing permissions and limitations
# under the License.
from collections import namedtuple
import re
from vitrage.evaluator.template_fields import TemplateFields
from vitrage.evaluator.template_schema_factory import TemplateSchemaFactory
@ -23,15 +22,6 @@ SYNTAX = 'syntax'
CONTENT = 'content'
def is_function(str):
"""Check if the string represents a function
A function has the format: func_name(params)
Search for a regex with open and close parenthesis
"""
return re.match('.*\(.*\)', str)
def get_template_schema(template):
metadata = template.get(TemplateFields.METADATA)
if metadata is None:

View File

@ -30,9 +30,9 @@ from vitrage.evaluator.actions.action_executor import ActionExecutor
from vitrage.evaluator.actions.base import ActionMode
from vitrage.evaluator.actions.base import ActionType
import vitrage.evaluator.actions.priority_tools as pt
from vitrage.evaluator.base import is_function
from vitrage.evaluator.template_data import ActionSpecs
from vitrage.evaluator.template_data import EdgeDescription
from vitrage.evaluator.template_functions.function_resolver import is_function
from vitrage.evaluator.template_schema_factory import TemplateSchemaFactory
from vitrage.graph.algo_driver.algorithm import Mapping
from vitrage.graph.algo_driver.sub_graph_matching import \

View File

@ -24,13 +24,9 @@ from vitrage.evaluator.base import get_template_schema
from vitrage.evaluator.base import Template
from vitrage.evaluator.base import TEMPLATE_LOADER
from vitrage.evaluator.equivalence_repository import EquivalenceRepository
from vitrage.evaluator.template_fields import TemplateFields
from vitrage.evaluator.template_loading.scenario_loader import ScenarioLoader
from vitrage.evaluator.template_validation.template_syntax_validator import \
EXCEPTION
from vitrage.graph.filter import check_filter as check_subset
from vitrage import storage
from vitrage.utils import file as file_utils
LOG = log.getLogger(__name__)
@ -167,17 +163,6 @@ class ScenarioRepository(object):
for t in templates:
self._add_template(t)
@staticmethod
def _load_template_file(file_name):
try:
config = file_utils.load_yaml_file(file_name,
with_exception=True)
if config:
return config
except Exception as e:
return {TemplateFields.METADATA: {TemplateFields.NAME: file_name},
EXCEPTION: str(e)}
@staticmethod
def _create_scenario_key(properties):
return frozenset(properties)

View File

@ -19,6 +19,7 @@ from vitrage.common.constants import TemplateTopologyFields as TFields
from vitrage.common.constants import TemplateTypes as TType
from vitrage.common.exception import VitrageError
from vitrage.evaluator.base import Template
from vitrage.evaluator.template_functions.v2 import resolve_parameters
from vitrage.evaluator import template_validation
from vitrage.evaluator.template_validation import base
from vitrage.storage.sqlalchemy import models
@ -29,7 +30,7 @@ METADATA = 'metadata'
NAME = 'name'
def add_templates_to_db(db, templates, cli_type):
def add_templates_to_db(db, templates, cli_type, params=None):
db_rows = list()
for template in templates:
final_type = template[METADATA].get(TFields.TYPE, cli_type)
@ -38,34 +39,47 @@ def add_templates_to_db(db, templates, cli_type):
"Unknown template type"))
continue
result = _validate_template(db, template, final_type)
if not _is_duplicate(db, template, result):
result = _validate_template(db, template, final_type, params)
if result.is_valid_config:
result = resolve_parameters(template, params)
# validate again, with the resolved parameters
if result.is_valid_config:
result = _validate_template(db, template, final_type)
# template_name might be a parameter, take it after resolve parameters
template_name = template.get(METADATA).get(NAME)
if _is_duplicate(db, template_name):
db_rows.append(_get_error_result(template, final_type,
"Duplicate template name"))
else:
db_row = _to_db_row(result, template, final_type)
db.templates.create(db_row)
db_rows.append(db_row)
else:
db_rows.append(_get_error_result(template, final_type,
"Duplicate template name"))
return db_rows
def validate_templates(db, templates, cli_type):
def validate_templates(db, templates, cli_type, params):
results = list()
for template in templates:
final_type = template[METADATA].get(TFields.TYPE, cli_type)
if not final_type or (cli_type and cli_type != final_type):
results.append(base.Result("", False, "", "Unknown template type"))
else:
results.append(_validate_template(db, template, final_type))
results.append(
_validate_template(db, template, final_type, params))
return results
def _validate_template(db, template, template_type):
def _validate_template(db, template, template_type, params=None):
if template_type == TType.DEFINITION:
result = template_validation.validate_definition_template(template)
elif template_type == TType.STANDARD:
result = template_validation.validate_template(template,
_load_def_templates(db))
_load_def_templates(db),
params)
elif template_type == TType.EQUIVALENCE:
result = base.Result("", True, "", "No Validation")
else:
@ -73,9 +87,8 @@ def _validate_template(db, template, template_type):
return result
def _is_duplicate(db, template, result):
if result.is_valid_config:
template_name = template[METADATA][NAME]
def _is_duplicate(db, template_name):
if template_name:
templates = db.templates.query(name=template_name)
if [t for t in templates if t.status != TemplateStatus.DELETED]:
return True

View File

@ -18,6 +18,7 @@ from vitrage.common.constants import TemplateTopologyFields
class TemplateFields(TemplateTopologyFields):
SCENARIOS = 'scenarios'
PARAMETERS = 'parameters'
ALARM_NAME = 'alarm_name'
ACTION = 'action'
@ -35,3 +36,4 @@ class TemplateFields(TemplateTopologyFields):
PROPERTIES = 'properties'
REGEX = '.regex'
DEFAULT = 'default'

View File

@ -0,0 +1,17 @@
# Copyright 2019 - 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.
# Function names
GET_ATTR = 'get_attr'
GET_PARAM = 'get_param'

View File

@ -0,0 +1,96 @@
# Copyright 2019 - 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 collections import namedtuple
from oslo_log import log
import re
import six
from vitrage.evaluator.template_validation.content.base import \
get_content_correct_result
from vitrage.evaluator.template_validation.content.base import \
get_content_fault_result
from vitrage.evaluator.template_validation.status_messages import status_msgs
LOG = log.getLogger(__name__)
FuncInfo = namedtuple('FuncInfo', ['name', 'func', 'error_code'])
class FunctionResolver(object):
@classmethod
def resolve_function(cls, func_info, template, **kwargs):
return cls._traverse_function(func_info, template, True, **kwargs)
@classmethod
def validate_function(cls, func_info, template, **kwargs):
return cls._traverse_function(func_info, template, False, **kwargs)
@classmethod
def _traverse_function(cls, func_info, template, resolve, **kwargs):
return cls._recursive_resolve_function(
func_info, template, template, resolve, **kwargs)
@classmethod
def _recursive_resolve_function(cls, func_info, template, template_block,
resolve, **kwargs):
result = get_content_correct_result()
for key, value in template_block.items():
if result.is_valid_config:
if isinstance(value, six.string_types) and \
_is_wanted_function(value, func_info.name):
func = func_info.func
if not func:
status = func_info.error_code
LOG.error('%s status code: %s' %
(status_msgs[status], status))
return get_content_fault_result(status)
result, resolved_value = func(value, template, **kwargs)
if result.is_valid_config and resolve:
template_block[key] = resolved_value
LOG.debug('Replaced %s with %s', value,
resolved_value)
elif isinstance(value, dict):
result = cls._recursive_resolve_function(
func_info, template, value, resolve, **kwargs)
elif isinstance(value, list):
for item in value:
if result.is_valid_config:
result = cls._recursive_resolve_function(
func_info, template, item, resolve, **kwargs)
return result
def is_function(str):
"""Check if the string represents a function
A function has the format: func_name(params)
Search for a regex with open and close parenthesis
"""
return re.match('.*\(.*\)', str)
def _is_wanted_function(str, func_name):
"""Check if the string represents `func_name` function
A function has the format: func_name(params)
Search for a regex with open and close parenthesis
"""
return re.match(func_name + '\(.*\)', str)

View File

@ -0,0 +1,43 @@
# Copyright 2019 - Nokia
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
from oslo_log import log
from vitrage.evaluator.template_functions.function_resolver import \
FuncInfo
from vitrage.evaluator.template_functions.function_resolver import \
FunctionResolver
from vitrage.evaluator.template_functions import GET_PARAM
from vitrage.evaluator.template_validation.content.base import \
get_content_correct_result
from vitrage.evaluator.template_validation.content.base import \
get_template_schema
LOG = log.getLogger(__name__)
def resolve_parameters(template_def, params=None):
if not params:
return get_content_correct_result()
result, template_schema = get_template_schema(template_def)
if not result.is_valid_config:
return result
get_param = template_schema.functions.get(GET_PARAM)
return FunctionResolver().resolve_function(
func_info=FuncInfo(name=GET_PARAM, func=get_param, error_code=160),
template=template_def,
actual_params=params)

View File

@ -13,10 +13,15 @@
# under the License.
from oslo_log import log
LOG = log.getLogger(__name__)
from vitrage.evaluator.template_fields import TemplateFields
from vitrage.evaluator.template_functions import GET_PARAM
from vitrage.evaluator.template_validation.content.base import \
get_content_correct_result
from vitrage.evaluator.template_validation.content.base import \
get_content_fault_result
from vitrage.evaluator.template_validation.status_messages import status_msgs
# Function names
GET_ATTR = 'get_attr'
LOG = log.getLogger(__name__)
def get_attr(match, *args):
@ -76,3 +81,93 @@ def get_attr(match, *args):
template_id, attr_name, str(entity_props), attr)
return attr
def get_param(param_name, template, **kwargs):
"""Return the value of a specific parameter that is used in a template
Usage: get_param(param_name, template, actual_params)
Example:
parameters:
new_state:
default: ERROR
scenarios:
- scenario:
condition: alarm_on_host
actions:
- action:
action_type: set_state
properties:
state: get_param(new_state)
action_target:
target: resource
actual_params may be empty or may define a new_state parameter:
{'new_state': 'SUBOPTIMAL'}
:param param_name: Name of a parameter
:param template: Complete template structure
:param kwargs: Additional arguments.
The expected argument is actual_params, a dict with key=value pairs of
parameter values.
:return: A tuple of (Result, param value)
The parameter value is taken from the actual_params, if given, or from the
default value that is defined in the template parameters section.
If none exists, a fault result is returned.
"""
param_defs = template.get(TemplateFields.PARAMETERS)
actual_params = kwargs.get('actual_params')
if not param_defs:
LOG.error('%s status code: %s' % (status_msgs[161], 161))
return get_content_fault_result(161), None
if param_name.startswith(GET_PARAM):
if not param_name.startswith(GET_PARAM + '(') or \
not param_name.endswith(')') or \
len(param_name) < len(GET_PARAM) + 3:
LOG.error('%s status code: %s' % (status_msgs[162], 162))
return get_content_fault_result(162), None
param_name = extract_param_name(param_name)
if not param_name:
LOG.error('%s status code: %s' % (status_msgs[162], 162))
return get_content_fault_result(162), None
# Make sure the parameter is defined in the parameters section
found_param_def = None
for param_key, param_value in param_defs.items():
if param_name == param_key:
found_param_def = param_key, param_value
if not found_param_def:
LOG.error('%s status code: %s' % (status_msgs[161], 161))
return get_content_fault_result(161), None
# Check if an actual value was assigned to this parameter
param_value = get_actual_value(param_name, actual_params)
if not param_value:
found_param_value = found_param_def[1]
default = found_param_value.get(TemplateFields.DEFAULT) \
if found_param_value else None # param_def may have a None value
if default:
param_value = default
else:
return get_content_fault_result(163), None
return get_content_correct_result(), param_value
def extract_param_name(param):
param_name = param[len(GET_PARAM):]
if len(param_name) > 2 and \
param_name[0] == '(' and param_name[-1] == ')':
param_name = param_name[1:-1]
return param_name
def get_actual_value(param_name, actual_params):
if actual_params:
return actual_params.get(param_name)

View File

@ -16,8 +16,10 @@ from oslo_log import log
from vitrage.evaluator.actions.base import ActionType
from vitrage.evaluator import base
from vitrage.evaluator.template_fields import TemplateFields
from vitrage.evaluator.template_functions import GET_ATTR
from vitrage.evaluator.template_functions import GET_PARAM
from vitrage.evaluator.template_functions.v2.functions import get_attr
from vitrage.evaluator.template_functions.v2.functions import GET_ATTR
from vitrage.evaluator.template_functions.v2.functions import get_param
from vitrage.evaluator.template_loading.template_loader import TemplateLoader
from vitrage.evaluator.template_loading.template_loader_v3 import\
TemplateLoader as V3TemplateLoader
@ -36,6 +38,8 @@ from vitrage.evaluator.template_validation.content.v1.definitions_validator \
from vitrage.evaluator.template_validation.content.v1.\
execute_mistral_validator import ExecuteMistralValidator as \
V1ExecuteMistralValidator
from vitrage.evaluator.template_validation.content.v1.get_param_validator \
import GetParamValidator as V1GetParamValidator
from vitrage.evaluator.template_validation.content.v1.mark_down_validator \
import MarkDownValidator
from vitrage.evaluator.template_validation.content.v1.metadata_validator \
@ -49,6 +53,8 @@ from vitrage.evaluator.template_validation.content.v1.set_state_validator \
from vitrage.evaluator.template_validation.content.v2.\
execute_mistral_validator import ExecuteMistralValidator as \
V2ExecuteMistralValidator
from vitrage.evaluator.template_validation.content.v2.get_param_validator \
import GetParamValidator as V2GetParamValidator
from vitrage.evaluator.template_validation.content.v2.metadata_validator \
import MetadataValidator as V2MetadataValidator
from vitrage.evaluator.template_validation.template_syntax_validator_v3 import\
@ -63,6 +69,7 @@ class TemplateSchema1(object):
TemplateFields.DEFINITIONS: DefinitionsValidator,
TemplateFields.METADATA: V1MetadataValidator,
TemplateFields.SCENARIOS: ScenarioValidator,
GET_PARAM: V1GetParamValidator,
ActionType.ADD_CAUSAL_RELATIONSHIP: AddCausalRelationshipValidator,
ActionType.EXECUTE_MISTRAL: V1ExecuteMistralValidator,
ActionType.MARK_DOWN: MarkDownValidator,
@ -92,8 +99,10 @@ class TemplateSchema2(TemplateSchema1):
self.validators[TemplateFields.METADATA] = V2MetadataValidator
self.validators[ActionType.EXECUTE_MISTRAL] = \
V2ExecuteMistralValidator
self.validators[GET_PARAM] = V2GetParamValidator
self.loaders[ActionType.EXECUTE_MISTRAL] = ActionLoader()
self.functions[GET_ATTR] = get_attr
self.functions[GET_PARAM] = get_param
def version(self):
return '2'

View File

@ -31,12 +31,12 @@ from vitrage.evaluator.template_validation.template_syntax_validator import \
LOG = log.getLogger(__name__)
def validate_template(template, def_templates):
def validate_template(template, def_templates, params=None):
result, template_schema = get_template_schema(template)
if not result.is_valid_config:
return result
if template_schema.version() < '3':
return _validate_template_v1_v2(template, def_templates)
return _validate_template_v1_v2(template, def_templates, params)
try:
template_schema.validators[SYNTAX].validate(template)
@ -48,12 +48,12 @@ def validate_template(template, def_templates):
return base.get_correct_result()
def _validate_template_v1_v2(template, def_templates):
def _validate_template_v1_v2(template, def_templates, params=None):
result = syntax_validation(template)
if not result.is_valid_config:
LOG.error('Unable to load template, syntax error: %s' % result.comment)
return result
result = content_validation(template, def_templates)
result = content_validation(template, def_templates, params)
if not result.is_valid_config:
LOG.error('Unable to load template, content error:%s' % result.comment)
return result

View File

@ -15,13 +15,14 @@
from oslo_log import log
from vitrage.evaluator.template_fields import TemplateFields
from vitrage.evaluator.template_functions import GET_PARAM
from vitrage.evaluator.template_validation.content.base import \
get_template_schema
LOG = log.getLogger(__name__)
def content_validation(template, def_templates=None):
def content_validation(template, def_templates=None, params=None):
if def_templates is None:
def_templates = {}
@ -43,6 +44,10 @@ def content_validation(template, def_templates=None):
else:
result.is_valid_config = False # Not supposed to happen
# Validate parameters
if result.is_valid_config:
result = parameters_validation(template_schema, template, params)
# Validate definitions
def_validator = \
template_schema.validators.get(TemplateFields.DEFINITIONS) \
@ -96,3 +101,9 @@ def content_validation(template, def_templates=None):
definitions_index, scenarios)
return result
def parameters_validation(template_schema, template, actual_params):
params_validator = \
template_schema.validators.get(GET_PARAM) if template_schema else None
return params_validator.validate(template, actual_params)

View File

@ -15,8 +15,8 @@
from oslo_log import log
from vitrage.evaluator.actions.recipes.execute_mistral import WORKFLOW
from vitrage.evaluator.base import is_function
from vitrage.evaluator.template_fields import TemplateFields
from vitrage.evaluator.template_functions.function_resolver import is_function
from vitrage.evaluator.template_validation.content.base import \
ActionValidator
from vitrage.evaluator.template_validation.content.base import \

View File

@ -0,0 +1,31 @@
# Copyright 2019 - 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_functions.function_resolver import \
FuncInfo
from vitrage.evaluator.template_functions.function_resolver import \
FunctionResolver
from vitrage.evaluator.template_functions import GET_PARAM
class GetParamValidator(object):
@classmethod
def validate(cls, template, actual_params):
# if there is a get_param in the template, an error message will be
# returned since func is None
return FunctionResolver().validate_function(
func_info=FuncInfo(name=GET_PARAM, func=None, error_code=160),
template=template,
actual_params=actual_params)

View File

@ -17,8 +17,8 @@ import re
from vitrage.evaluator.actions.recipes.execute_mistral import INPUT
from vitrage.evaluator.actions.recipes.execute_mistral import WORKFLOW
from vitrage.evaluator.base import is_function
from vitrage.evaluator.template_fields import TemplateFields
from vitrage.evaluator.template_functions.function_resolver import is_function
from vitrage.evaluator.template_validation.content.base import \
ActionValidator
from vitrage.evaluator.template_validation.content.base import \

View File

@ -0,0 +1,30 @@
# Copyright 2019 - 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_functions.function_resolver import \
FuncInfo
from vitrage.evaluator.template_functions.function_resolver import \
FunctionResolver
from vitrage.evaluator.template_functions import GET_PARAM
from vitrage.evaluator.template_functions.v2.functions import get_param
class GetParamValidator(object):
@classmethod
def validate(cls, template, actual_params):
return FunctionResolver().validate_function(
func_info=FuncInfo(name=GET_PARAM, func=get_param, error_code=160),
template=template,
actual_params=actual_params)

View File

@ -100,6 +100,14 @@ status_msgs = {
143: 'A template definition file cannot contain \'includes\' or '
'\'scenarios\' blocks',
# parameters status messages 160-179
160: 'Parameters are supported only from version 2',
161: 'get_param called for a parameter that is not defined in the '
'\'parameters\' block',
162: 'malformed get_param() clause',
163: 'Failed to resolve parameter',
# template version 3
10100: 'Action must contain a \'target\' property',
10101: 'Action \'target\' must match an entity id',
10102: 'Action must contain a \'source\' property',

View File

@ -95,19 +95,29 @@ def _validate_def_template_template_sections(def_template_conf):
def _validate_template_sections(template_conf):
any_str = Any(str, six.text_type)
paramsSchema = Schema({
any_str: Any(any_str, Schema({
Optional(TemplateFields.DESCRIPTION): any_str,
Optional(TemplateFields.DEFAULT): any_str,
})),
})
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
Optional(TemplateFields.INCLUDES): list,
Optional(TemplateFields.PARAMETERS): paramsSchema,
})
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
Optional(TemplateFields.INCLUDES): list,
Optional(TemplateFields.PARAMETERS): paramsSchema,
})
return _validate_dict_schema(schema, template_conf)

View File

@ -26,8 +26,8 @@ from vitrage.common.constants import TemplateTypes
from vitrage.evaluator.actions.base import ActionType
from vitrage.evaluator.actions.recipes.execute_mistral import INPUT
from vitrage.evaluator.actions.recipes.execute_mistral import WORKFLOW
from vitrage.evaluator.base import is_function
from vitrage.evaluator.template_fields import TemplateFields as TF
from vitrage.evaluator.template_functions.function_resolver import is_function
from vitrage.evaluator.template_schema_factory import TemplateSchemaFactory
LOG = log.getLogger(__name__)

View File

@ -0,0 +1,336 @@
# Copyright 2019 - Nokia
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import json
from oslo_config import cfg
from testtools import matchers
from vitrage.api_handler.apis.template import TemplateApis
from vitrage.tests.functional.test_configuration import TestConfiguration
from vitrage.tests.mocks import utils
from vitrage.tests.unit.entity_graph.base import TestEntityGraphUnitBase
class TestTemplates(TestEntityGraphUnitBase, TestConfiguration):
TEMPLATE_WITH_PARAMS = 'with_params.yaml'
TEMPLATE_WITH_EXTRA_PARAM_DEF = 'with_extra_param_def.yaml'
TEMPLATE_WITH_MISSING_PARAM_DEF = 'with_missing_param_def.yaml'
TEMPLATE_WITHOUT_PARAMS = 'without_params.yaml'
VALIDATION_FAILED = 'validation failed'
VALIDATION_OK = 'validation OK'
class MockNotifier(object):
def notify(self, event_type, data):
pass
@classmethod
def setUpClass(cls):
super(TestTemplates, cls).setUpClass()
cls.conf = cfg.ConfigOpts()
cls.add_db(cls.conf)
cls.apis = TemplateApis(notifier=cls.MockNotifier(), db=cls._db)
cls.added_template = None
def tearDown(self):
super(TestTemplates, self).tearDown()
self._delete_templates()
def test_validate_template_with_no_params(self):
# Setup
template_path = '%s/templates/parameters/%s' % (
utils.get_resources_dir(),
self.TEMPLATE_WITH_PARAMS)
files_content = [(template_path, self._load_yaml_file(template_path))]
# Action
results = self.apis.validate_template(
ctx=None, templates=files_content, template_type=None, params=None)
# Test assertions
self._assert_validate_template_result(
self.VALIDATION_FAILED, 163,
'Failed to resolve parameter', results)
def test_validate_template_with_missing_param(self):
# Setup
apis = TemplateApis(notifier=self.MockNotifier(), db=self._db)
template_path = '%s/templates/parameters/%s' % (
utils.get_resources_dir(),
self.TEMPLATE_WITH_PARAMS)
files_content = [(template_path, self._load_yaml_file(template_path))]
params = {'template_name': 'template_with_params_1',
'alarm_name': 'My alarm', 'new_state': 'SUBOPTIMAL'}
# Action
results = apis.validate_template(ctx=None, templates=files_content,
template_type=None, params=params)
# Test assertions
self._assert_validate_template_result(
self.VALIDATION_FAILED, 163,
'Failed to resolve parameter', results)
def test_validate_template_with_actual_params(self):
# Setup
apis = TemplateApis(notifier=self.MockNotifier(), db=self._db)
template_path = '%s/templates/parameters/%s' % (
utils.get_resources_dir(),
self.TEMPLATE_WITH_PARAMS)
files_content = [(template_path, self._load_yaml_file(template_path))]
params = {'template_name': 'template_with_params_2',
'alarm_type': 'zabbix', 'alarm_name': 'My alarm',
'new_state': 'SUBOPTIMAL'}
# Action
results = apis.validate_template(ctx=None, templates=files_content,
template_type=None, params=params)
# Test assertions
self._assert_validate_template_result(
self.VALIDATION_OK, 0, 'Template validation is OK', results)
def test_validate_template_with_missing_param_def(self):
# Setup
apis = TemplateApis(notifier=self.MockNotifier(), db=self._db)
template_path = '%s/templates/parameters/%s' % (
utils.get_resources_dir(),
self.TEMPLATE_WITH_MISSING_PARAM_DEF)
files_content = [(template_path, self._load_yaml_file(template_path))]
params = {'alarm_type': 'zabbix', 'alarm_name': 'My alarm',
'new_state': 'SUBOPTIMAL'}
# Action
results = apis.validate_template(ctx=None, templates=files_content,
template_type=None, params=params)
# Test assertions
self._assert_validate_template_result(
self.VALIDATION_FAILED, 161, 'get_param called for a parameter '
'that is not defined in the \'parameters\' block', results)
def test_validate_template_without_params(self):
# Setup
apis = TemplateApis(notifier=self.MockNotifier(), db=self._db)
template_path = '%s/templates/parameters/%s' % (
utils.get_resources_dir(),
self.TEMPLATE_WITHOUT_PARAMS)
files_content = [(template_path, self._load_yaml_file(template_path))]
# Action
results = apis.validate_template(ctx=None, templates=files_content,
template_type=None, params=None)
# Test assertions
self._assert_validate_template_result(
self.VALIDATION_OK, 0, 'Template validation is OK', results)
def test_validate_template_with_extra_actual_param(self):
# Setup
apis = TemplateApis(notifier=self.MockNotifier(), db=self._db)
template_path = '%s/templates/parameters/%s' % (
utils.get_resources_dir(),
self.TEMPLATE_WITH_PARAMS)
files_content = [(template_path, self._load_yaml_file(template_path))]
params = {'template_name': 'template_with_params_2',
'alarm_type': 'zabbix', 'alarm_name': 'My alarm',
'new_state': 'SUBOPTIMAL',
'non_existing_param': 'some value'}
# Action
results = apis.validate_template(ctx=None, templates=files_content,
template_type=None, params=params)
# Test assertions
self._assert_validate_template_result(
self.VALIDATION_OK, 0, 'Template validation is OK', results)
def test_validate_template_with_extra_param_def(self):
# Setup
apis = TemplateApis(notifier=self.MockNotifier(), db=self._db)
template_path = '%s/templates/parameters/%s' % (
utils.get_resources_dir(),
self.TEMPLATE_WITH_EXTRA_PARAM_DEF)
files_content = [(template_path, self._load_yaml_file(template_path))]
params = {'template_name': 'template_with_params_2',
'alarm_type': 'zabbix', 'alarm_name': 'My alarm',
'new_state': 'SUBOPTIMAL'}
# Action
results = apis.validate_template(ctx=None, templates=files_content,
template_type=None, params=params)
# Test assertions
self._assert_validate_template_result(
self.VALIDATION_OK, 0, 'Template validation is OK', results)
def test_add_template_with_no_params(self):
# Setup
template_path = '%s/templates/parameters/%s' % (
utils.get_resources_dir(),
self.TEMPLATE_WITH_PARAMS)
files_content = [(template_path, self._load_yaml_file(template_path))]
# Action.
added_templates = \
self.apis.add_template(ctx=None, templates=files_content,
template_type=None, params=None)
self.added_template = added_templates[0]['uuid']
# Test assertions
self.assertThat(added_templates, matchers.HasLength(1))
self.assertEqual('ERROR', added_templates[0]['status'])
self.assertEqual('Failed to resolve parameter',
added_templates[0]['status details'])
def test_add_template_with_missing_param(self):
# Setup
template_path = '%s/templates/parameters/%s' % (
utils.get_resources_dir(),
self.TEMPLATE_WITH_PARAMS)
files_content = [(template_path, self._load_yaml_file(template_path))]
params = {'template_name': 'template_with_params_3',
'alarm_name': 'My alarm', 'new_state': 'SUBOPTIMAL'}
# Action
added_templates = \
self.apis.add_template(ctx=None, templates=files_content,
template_type=None, params=params)
self.added_template = added_templates[0]['uuid']
# Test assertions
self.assertThat(added_templates, matchers.HasLength(1))
self.assertEqual('ERROR', added_templates[0]['status'])
self.assertEqual('Failed to resolve parameter',
added_templates[0]['status details'])
def test_add_template_with_actual_params(self):
# Setup
template_path = '%s/templates/parameters/%s' % (
utils.get_resources_dir(),
self.TEMPLATE_WITH_PARAMS)
files_content = [(template_path, self._load_yaml_file(template_path))]
params = {'template_name': 'template_with_params_4',
'alarm_type': 'zabbix', 'alarm_name': 'My alarm',
'new_state': 'SUBOPTIMAL'}
# Action
added_templates = \
self.apis.add_template(ctx=None, templates=files_content,
template_type=None, params=params)
self.added_template = added_templates[0]['uuid']
# Test assertions
self.assertThat(added_templates, matchers.HasLength(1))
self.assertEqual('LOADING', added_templates[0]['status'])
def test_add_template_with_missing_param_def(self):
# Setup
template_path = '%s/templates/parameters/%s' % (
utils.get_resources_dir(),
self.TEMPLATE_WITH_MISSING_PARAM_DEF)
files_content = [(template_path, self._load_yaml_file(template_path))]
params = {'alarm_type': 'zabbix', 'alarm_name': 'My alarm',
'new_state': 'SUBOPTIMAL'}
# Action
added_templates = \
self.apis.add_template(ctx=None, templates=files_content,
template_type=None, params=params)
self.added_template = added_templates[0]['uuid']
# Test assertions
self.assertEqual('ERROR', added_templates[0]['status'])
self.assertEqual('get_param called for a parameter that is not '
'defined in the \'parameters\' block',
added_templates[0]['status details'])
def test_add_template_without_params(self):
# Setup
template_path = '%s/templates/parameters/%s' % (
utils.get_resources_dir(),
self.TEMPLATE_WITHOUT_PARAMS)
files_content = [(template_path, self._load_yaml_file(template_path))]
# Action
added_templates = \
self.apis.add_template(ctx=None, templates=files_content,
template_type=None, params=None)
self.added_template = added_templates[0]['uuid']
# Test assertions
self.assertThat(added_templates, matchers.HasLength(1))
self.assertEqual('LOADING', added_templates[0]['status'])
def test_add_template_with_extra_actual_param(self):
# Setup
template_path = '%s/templates/parameters/%s' % (
utils.get_resources_dir(),
self.TEMPLATE_WITH_PARAMS)
files_content = [(template_path, self._load_yaml_file(template_path))]
params = {'template_name': 'template_with_extra_actual_param',
'alarm_type': 'zabbix', 'alarm_name': 'My alarm',
'new_state': 'SUBOPTIMAL',
'non_existing_param': 'some value'}
# Action
added_templates = \
self.apis.add_template(ctx=None, templates=files_content,
template_type=None, params=params)
self.added_template = added_templates[0]['uuid']
# Test assertions
self.assertThat(added_templates, matchers.HasLength(1))
self.assertEqual('LOADING', added_templates[0]['status'])
def test_add_template_with_extra_param_def(self):
# Setup
template_path = '%s/templates/parameters/%s' % (
utils.get_resources_dir(),
self.TEMPLATE_WITH_EXTRA_PARAM_DEF)
files_content = [(template_path, self._load_yaml_file(template_path))]
params = {'template_name': 'template_with_extra_param_def',
'alarm_type': 'zabbix', 'alarm_name': 'My alarm',
'new_state': 'SUBOPTIMAL'}
# Action
added_templates = \
self.apis.add_template(ctx=None, templates=files_content,
template_type=None, params=params)
self.added_template = added_templates[0]['uuid']
# Test assertions
self.assertThat(added_templates, matchers.HasLength(1))
self.assertEqual('LOADING', added_templates[0]['status'])
def _assert_validate_template_result(self, expected_status,
expected_status_code,
expected_message, results):
self.assertIsNotNone(results)
results = json.loads(results)
results = results['results']
self.assertIsNotNone(results)
self.assertThat(results, matchers.HasLength(1))
self.assertEqual(expected_status, results[0]['status'])
self.assertEqual(expected_status_code, results[0]['status code'])
self.assertEqual(expected_message, results[0]['message'])
def _delete_templates(self):
if self.added_template:
self.apis.delete_template(ctx=None, uuids=[self.added_template])

View File

@ -0,0 +1,43 @@
metadata:
version: 2
type: standard
name: get_param(template_name)
description: template with extra parameter def (extra_param is unused)
parameters:
template_name:
description: the name of the template
default: template_with_params
alarm_type:
description: the type of the alarm
alarm_name:
new_state:
default: ERROR
extra_param:
description: a parameter definition that is unused in the template
definitions:
entities:
- entity:
category: ALARM
type: get_param(alarm_type)
name: get_param(alarm_name)
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
scenarios:
- scenario:
condition: alarm_on_host
actions:
- action:
action_type: set_state
properties:
state: get_param(new_state)
action_target:
target: resource

View File

@ -0,0 +1,37 @@
metadata:
version: 2
type: standard
name: template_with_missing_param_def
description: INVALID template with missing parameter def for alarm_name
parameters:
alarm_type:
description: the type of the alarm
new_state:
default: ERROR
definitions:
entities:
- entity:
category: ALARM
type: get_param(alarm_type)
name: get_param(alarm_name)
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
scenarios:
- scenario:
condition: alarm_on_host
actions:
- action:
action_type: set_state
properties:
state: get_param(new_state)
action_target:
target: resource

View File

@ -0,0 +1,41 @@
metadata:
version: 2
type: standard
name: get_param(template_name)
description: template with parameters
parameters:
template_name:
description: the name of the template
default: template_with_params
alarm_type:
description: the type of the alarm
alarm_name:
new_state:
default: ERROR
definitions:
entities:
- entity:
category: ALARM
type: get_param(alarm_type)
name: get_param(alarm_name)
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
scenarios:
- scenario:
condition: alarm_on_host
actions:
- action:
action_type: set_state
properties:
state: get_param(new_state)
action_target:
target: resource

View File

@ -0,0 +1,32 @@
metadata:
version: 2
type: standard
name: template_without_params
description: template without parameters
definitions:
entities:
- entity:
category: ALARM
type: zabbix
name: Something went wrong!
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
scenarios:
- scenario:
condition: alarm_on_host
actions:
- action:
action_type: set_state
properties:
state: SUBOPTIMAL
action_target:
target: resource

View File

@ -0,0 +1,40 @@
# Copyright 2019 - 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.content.v1.get_param_validator \
import GetParamValidator
from vitrage.tests.unit.evaluator.template_validation.content.base import \
ValidatorTest
class ParametersValidatorTest(ValidatorTest):
"""Tests for the parameters validator of version 1
All tests should succeed, as long as there is no get_param reference in
the template itself
"""
def test_validate_no_parameters(self):
result = GetParamValidator.validate(template={}, actual_params=None)
self._assert_correct_result(result)
def test_validate_empty_parameters(self):
result = GetParamValidator.validate(template={}, actual_params={})
self._assert_correct_result(result)
def test_validate_with_parameter(self):
template = {'alarm_name': 'get_param(param1)'}
result = \
GetParamValidator.validate(template=template, actual_params={})
self._assert_fault_result(result, 160)

View File

@ -0,0 +1,200 @@
# Copyright 2019 - 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
from vitrage.evaluator.template_functions.v2.functions import get_param
from vitrage.evaluator.template_validation.content.v2.get_param_validator \
import GetParamValidator
from vitrage.tests.unit.evaluator.template_validation.content.base import \
ValidatorTest
class ParametersValidatorTest(ValidatorTest):
def test_validate_no_parameters(self):
template = {'no parameters in this template': 'at all'}
result = \
GetParamValidator.validate(template=template, actual_params={})
self._assert_correct_result(result)
def test_validate_empty_parameters(self):
template = {'parameters': ''}
result = \
GetParamValidator.validate(template=template, actual_params={})
self._assert_correct_result(result)
def test_validate_single_parameter(self):
template = {
'parameters': {
'single_param': {
TemplateFields.DESCRIPTION: 'blabla',
TemplateFields.DEFAULT: 'this is my default'
}
}
}
result = \
GetParamValidator.validate(template=template, actual_params={})
self._assert_correct_result(result)
def test_validate_few_parameters(self):
template = {
'parameters': {
'param1': {
TemplateFields.DESCRIPTION: 'blabla',
TemplateFields.DEFAULT: 'this is my default'
},
'param2': {
TemplateFields.DESCRIPTION: 'blabla2',
},
'param3': {
TemplateFields.DEFAULT: 'this is my default3'
},
'param4': {
},
}
}
result = \
GetParamValidator.validate(template=template, actual_params={})
self._assert_correct_result(result)
def test_validate_get_param_with_no_parameters(self):
template = {'alarm_name': 'get_param(param1)'}
result, _ = get_param('get_param(param1)', template)
self._assert_fault_result(result, 161)
def test_validate_get_param_with_empty_parameters(self):
template = {}
result, _ = get_param('get_param(param1)', template)
self._assert_fault_result(result, 161)
def test_validate_get_param_with_undefined_parameter(self):
template = {
'parameters': {
'param1': {
TemplateFields.DESCRIPTION: 'blabla',
TemplateFields.DEFAULT: 'this is my default'
},
'param2': {
TemplateFields.DESCRIPTION: 'blabla2',
},
}
}
result, _ = get_param('get_param(undefined)', template)
self._assert_fault_result(result, 161)
def test_validate_get_param_with_valid_parameter(self):
template = {
'parameters': {
'param1': {
TemplateFields.DESCRIPTION: 'blabla',
TemplateFields.DEFAULT: 'this is my default'
},
'param2': {
TemplateFields.DESCRIPTION: 'blabla2',
},
}
}
result, result_value = get_param('get_param(param1)', template)
self._assert_correct_result(result)
def test_validate_get_param_with_malformed_parameter(self):
template = {
'parameters': {
'param1': {
TemplateFields.DESCRIPTION: 'blabla',
TemplateFields.DEFAULT: 'this is my default'
},
'param2': {
TemplateFields.DESCRIPTION: 'blabla2',
},
}
}
result, _ = get_param('get_param(param1', template)
self._assert_fault_result(result, 162)
result, _ = get_param('get_paramparam1)', template)
self._assert_fault_result(result, 162)
result, _ = get_param('get_paramparam1', template)
self._assert_fault_result(result, 162)
result, _ = get_param('get_param', template)
self._assert_fault_result(result, 162)
result, _ = get_param('get_param()', template)
self._assert_fault_result(result, 162)
result, _ = get_param('get_param)param1(', template)
self._assert_fault_result(result, 162)
def test_validate_get_param_with_actual_parameter(self):
template = {
'parameters': {
'param1': {
TemplateFields.DESCRIPTION: 'blabla',
TemplateFields.DEFAULT: 'this is my default'
},
'param2': {
TemplateFields.DESCRIPTION: 'blabla2',
},
}
}
actual_params = {
'param1': 'value1',
'param2': 'value2'
}
result, result_value = get_param('get_param(param2)', template,
actual_params=actual_params)
self._assert_correct_result(result)
self.assertEqual('value2', result_value)
def test_validate_get_param_with_missing_actual_parameter(self):
template = {
'parameters': {
'param1': {
TemplateFields.DESCRIPTION: 'blabla',
TemplateFields.DEFAULT: 'this is my default'
},
'param2': {
TemplateFields.DESCRIPTION: 'blabla2',
},
}
}
actual_params = {
'param1': 'value1',
}
result, _ = get_param('get_param(param2)', template,
actual_params=actual_params)
self._assert_fault_result(result, 163)
def test_validate_get_param_with_default_actual_parameter(self):
template = {
'parameters': {
'param1': {
TemplateFields.DESCRIPTION: 'blabla',
TemplateFields.DEFAULT: 'this is my default'
},
'param2': {
TemplateFields.DESCRIPTION: 'blabla2',
},
}
}
actual_params = {
'param2': 'value2',
}
result, result_value = get_param('get_param(param1)', template,
actual_params=actual_params)
self._assert_correct_result(result)
self.assertEqual('this is my default', result_value)

View File

@ -14,10 +14,11 @@
import copy
import logging
from vitrage.evaluator.scenario_repository import ScenarioRepository
from vitrage.evaluator.template_fields import TemplateFields
from vitrage.evaluator.template_validation.status_messages import status_msgs
from vitrage.evaluator.template_validation import template_syntax_validator
from vitrage.evaluator.template_validation.template_syntax_validator import \
EXCEPTION
from vitrage.tests import base
from vitrage.tests.mocks import utils
from vitrage.utils import file as file_utils
@ -41,8 +42,7 @@ class TemplateSyntaxValidatorTest(base.BaseTest):
% utils.get_resources_dir()
cls.template_yamls = file_utils.load_yaml_files(template_dir_path)
cls.bad_template = \
ScenarioRepository._load_template_file(template_dir_path
+ '/' + BAD_YAML_PATH)
cls._load_template_file(template_dir_path + '/' + BAD_YAML_PATH)
cls.first_template = cls.template_yamls[0]
cls._hide_useless_logging_messages()
@ -291,3 +291,14 @@ class TemplateSyntaxValidatorTest(base.BaseTest):
'template_syntax_validator'
syntax_validator_log = logging.getLogger(validator_path)
syntax_validator_log.setLevel(logging.FATAL)
@staticmethod
def _load_template_file(file_name):
try:
config = file_utils.load_yaml_file(file_name,
with_exception=True)
if config:
return config
except Exception as e:
return {TemplateFields.METADATA: {TemplateFields.NAME: file_name},
EXCEPTION: str(e)}