Merge "Support parameters in Vitrage templates"
This commit is contained in:
commit
461875fc69
@ -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…
x
Reference in New Issue
Block a user