Merge "Support parameters in Vitrage templates"

This commit is contained in:
Zuul 2019-02-15 14:48:34 +00:00 committed by Gerrit Code Review
commit 461875fc69
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)}