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:
parent
dd79ed9d52
commit
76fd30fd13
@ -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.
|
@ -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.')
|
||||
|
@ -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'})
|
||||
|
@ -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:
|
||||
|
@ -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 \
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
@ -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'
|
||||
|
@ -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'
|
96
vitrage/evaluator/template_functions/function_resolver.py
Normal file
96
vitrage/evaluator/template_functions/function_resolver.py
Normal 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)
|
@ -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)
|
@ -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)
|
||||
|
@ -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'
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
|
@ -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 \
|
||||
|
@ -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)
|
@ -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 \
|
||||
|
@ -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)
|
@ -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',
|
||||
|
@ -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)
|
||||
|
||||
|
@ -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__)
|
||||
|
336
vitrage/tests/functional/api_handler/test_templates.py
Normal file
336
vitrage/tests/functional/api_handler/test_templates.py
Normal 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])
|
@ -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
|
@ -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
|
@ -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
|
@ -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
|
@ -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)
|
@ -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)
|
@ -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)}
|
||||
|
Loading…
Reference in New Issue
Block a user