Merge "Support per-version template loading + change execute_mistral structure"
This commit is contained in:
commit
4ff1908d1b
@ -117,6 +117,9 @@ The following describes all the possible status code and their messages:
|
|||||||
| 135 | condition must contain a common entity for all 'or' | content |
|
| 135 | condition must contain a common entity for all 'or' | content |
|
||||||
| | clauses | |
|
| | clauses | |
|
||||||
+------------------+---------------------------------------------------------+-------------------------------+
|
+------------------+---------------------------------------------------------+-------------------------------+
|
||||||
|
| 136 | Input parameters for Mistral workflow in execute_mistral| content (version 2) |
|
||||||
|
| | action must be placed under an 'input' block | |
|
||||||
|
+------------------+---------------------------------------------------------+-------------------------------+
|
||||||
| 140 | At least one template must be included | syntax |
|
| 140 | At least one template must be included | syntax |
|
||||||
+------------------+---------------------------------------------------------+-------------------------------+
|
+------------------+---------------------------------------------------------+-------------------------------+
|
||||||
| 141 | Name field is unspecified for include | syntax |
|
| 141 | Name field is unspecified for include | syntax |
|
||||||
|
@ -11,6 +11,12 @@ This page describes the format of the Vitrage templates, with some examples and
|
|||||||
open questions on extending this format. Additionally, a short guide on adding
|
open questions on extending this format. Additionally, a short guide on adding
|
||||||
templates is presented.
|
templates is presented.
|
||||||
|
|
||||||
|
*Note:* This document refers to Vitrage templates version 2. The documentation
|
||||||
|
of version 1 can be found here_
|
||||||
|
|
||||||
|
.. _here: https://docs.openstack.org/vitrage/pike/
|
||||||
|
|
||||||
|
|
||||||
Template Structure
|
Template Structure
|
||||||
==================
|
==================
|
||||||
The template is written in YAML language, with the following structure:
|
The template is written in YAML language, with the following structure:
|
||||||
@ -463,9 +469,10 @@ its parameters.
|
|||||||
action:
|
action:
|
||||||
action_type: execute_mistral
|
action_type: execute_mistral
|
||||||
properties:
|
properties:
|
||||||
workflow: demo_workflow # mandatory. The name of the workflow to be executed
|
workflow: demo_workflow # mandatory. The name of the workflow to be executed
|
||||||
farewell: Goodbye and Good Luck! # optional. A list of properties to be passed to the workflow
|
input: # optional. A list of properties to be passed to the workflow
|
||||||
employee: John Smith # optional. A list of properties to be passed to the workflow
|
farewell: Goodbye and Good Luck!
|
||||||
|
employee: John Smith
|
||||||
|
|
||||||
|
|
||||||
Future support & Open Issues
|
Future support & Open Issues
|
||||||
|
@ -0,0 +1,6 @@
|
|||||||
|
---
|
||||||
|
features:
|
||||||
|
- Refactored the ``execute-mistral`` action. All input parameters should
|
||||||
|
appear under an ``input`` section. The change takes effect in template
|
||||||
|
version 2. ``execute-mistral`` actions from version 1 are automatically
|
||||||
|
converted to the new format.
|
@ -14,6 +14,8 @@
|
|||||||
|
|
||||||
from oslo_config import cfg
|
from oslo_config import cfg
|
||||||
|
|
||||||
|
from vitrage.evaluator.template_schemas import init_template_schemas
|
||||||
|
|
||||||
|
|
||||||
# Register options for the service
|
# Register options for the service
|
||||||
OPTS = [
|
OPTS = [
|
||||||
@ -38,3 +40,5 @@ OPTS = [
|
|||||||
'determined, else a default worker count of 1 is returned.'
|
'determined, else a default worker count of 1 is returned.'
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
|
|
||||||
|
init_template_schemas()
|
||||||
|
@ -18,6 +18,7 @@ from vitrage.evaluator.actions.recipes.base import ActionStepWrapper
|
|||||||
|
|
||||||
|
|
||||||
MISTRAL = 'mistral'
|
MISTRAL = 'mistral'
|
||||||
|
INPUT = 'input'
|
||||||
WORKFLOW = 'workflow'
|
WORKFLOW = 'workflow'
|
||||||
|
|
||||||
|
|
||||||
|
@ -20,6 +20,7 @@ from oslo_log import log
|
|||||||
|
|
||||||
from vitrage.common.constants import EdgeProperties as EProps
|
from vitrage.common.constants import EdgeProperties as EProps
|
||||||
from vitrage.common.constants import VertexProperties as VProps
|
from vitrage.common.constants import VertexProperties as VProps
|
||||||
|
from vitrage.common.utils import recursive_keypairs
|
||||||
from vitrage.datasources.listener_service import defaultdict
|
from vitrage.datasources.listener_service import defaultdict
|
||||||
from vitrage.entity_graph.mappings.datasource_info_mapper \
|
from vitrage.entity_graph.mappings.datasource_info_mapper \
|
||||||
import DatasourceInfoMapper
|
import DatasourceInfoMapper
|
||||||
@ -238,11 +239,19 @@ class ScenarioEvaluator(EvaluatorBase):
|
|||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _generate_action_id(action_spec):
|
def _generate_action_id(action_spec):
|
||||||
|
"""Generate a unique action id for the action
|
||||||
|
|
||||||
|
BEWARE: The implementation of this function MUST NOT BE CHANGED!!
|
||||||
|
|
||||||
|
The created hash is used for storing the active actions in the
|
||||||
|
database. If changed, existing active actions can no longer be
|
||||||
|
retrieved.
|
||||||
|
"""
|
||||||
targets = [(k, v.vertex_id) for k, v in action_spec.targets.items()]
|
targets = [(k, v.vertex_id) for k, v in action_spec.targets.items()]
|
||||||
return hash(
|
return hash(
|
||||||
(action_spec.type,
|
(action_spec.type,
|
||||||
tuple(sorted(targets)),
|
tuple(sorted(targets)),
|
||||||
tuple(sorted(action_spec.properties.items())))
|
tuple(sorted(recursive_keypairs(action_spec.properties))))
|
||||||
)
|
)
|
||||||
|
|
||||||
def _analyze_and_filter_actions(self, actions):
|
def _analyze_and_filter_actions(self, actions):
|
||||||
|
@ -25,8 +25,8 @@ from vitrage.evaluator.equivalence_repository import EquivalenceRepository
|
|||||||
from vitrage.evaluator.template_fields import TemplateFields
|
from vitrage.evaluator.template_fields import TemplateFields
|
||||||
from vitrage.evaluator.template_loading.scenario_loader import ScenarioLoader
|
from vitrage.evaluator.template_loading.scenario_loader import ScenarioLoader
|
||||||
from vitrage.evaluator.template_loading.template_loader import TemplateLoader
|
from vitrage.evaluator.template_loading.template_loader import TemplateLoader
|
||||||
from vitrage.evaluator.template_validation.content.definitions_validator \
|
from vitrage.evaluator.template_validation.content.base import \
|
||||||
import DefinitionsValidator as DefValidator
|
get_template_schema
|
||||||
from vitrage.evaluator.template_validation.content.template_content_validator \
|
from vitrage.evaluator.template_validation.content.template_content_validator \
|
||||||
import content_validation
|
import content_validation
|
||||||
from vitrage.evaluator.template_validation.template_syntax_validator import \
|
from vitrage.evaluator.template_validation.template_syntax_validator import \
|
||||||
@ -135,23 +135,30 @@ class ScenarioRepository(object):
|
|||||||
self._add_scenario(equivalent_scenario)
|
self._add_scenario(equivalent_scenario)
|
||||||
|
|
||||||
def add_def_template(self, def_template):
|
def add_def_template(self, def_template):
|
||||||
|
result, template_schema = get_template_schema(def_template)
|
||||||
|
|
||||||
result = def_template_syntax_validation(def_template)
|
if result.is_valid_config:
|
||||||
if not result.is_valid_config:
|
result = def_template_syntax_validation(def_template)
|
||||||
LOG.info('Unable to load definition template, syntax err: %s'
|
|
||||||
% result.comment)
|
|
||||||
else:
|
|
||||||
result = DefValidator.def_template_content_validation(def_template)
|
|
||||||
if not result.is_valid_config:
|
if not result.is_valid_config:
|
||||||
LOG.info('Unable to load definition template, content err: %s'
|
LOG.info('Unable to load definition template, syntax err: %s'
|
||||||
% result.comment)
|
% result.comment)
|
||||||
|
|
||||||
current_time = datetime_utils.utcnow()
|
if result.is_valid_config:
|
||||||
include_uuid = uuidutils.generate_uuid()
|
def_validator = \
|
||||||
self._def_templates[str(include_uuid)] = Template(include_uuid,
|
template_schema.validator(TemplateFields.DEFINITIONS)
|
||||||
def_template,
|
result = \
|
||||||
current_time,
|
def_validator.def_template_content_validation(def_template)
|
||||||
result)
|
|
||||||
|
if result.is_valid_config:
|
||||||
|
current_time = datetime_utils.utcnow()
|
||||||
|
include_uuid = uuidutils.generate_uuid()
|
||||||
|
self._def_templates[str(include_uuid)] = Template(include_uuid,
|
||||||
|
def_template,
|
||||||
|
current_time,
|
||||||
|
result)
|
||||||
|
else:
|
||||||
|
LOG.info('Unable to load definition template, content err: %s'
|
||||||
|
% result.comment)
|
||||||
|
|
||||||
def _expand_equivalence(self, scenario):
|
def _expand_equivalence(self, scenario):
|
||||||
equivalent_scenarios = [scenario]
|
equivalent_scenarios = [scenario]
|
||||||
|
@ -15,10 +15,6 @@
|
|||||||
from vitrage.common.constants import TemplateTopologyFields
|
from vitrage.common.constants import TemplateTopologyFields
|
||||||
|
|
||||||
|
|
||||||
DEFAULT_VERSION = '1'
|
|
||||||
SUPPORTED_VERSIONS = ['1']
|
|
||||||
|
|
||||||
|
|
||||||
class TemplateFields(TemplateTopologyFields):
|
class TemplateFields(TemplateTopologyFields):
|
||||||
|
|
||||||
SCENARIOS = 'scenarios'
|
SCENARIOS = 'scenarios'
|
||||||
|
@ -12,24 +12,29 @@
|
|||||||
# License for the specific language governing permissions and limitations
|
# License for the specific language governing permissions and limitations
|
||||||
# under the License.
|
# under the License.
|
||||||
|
|
||||||
|
from oslo_log import log
|
||||||
|
|
||||||
from vitrage.common.exception import VitrageError
|
from vitrage.common.exception import VitrageError
|
||||||
from vitrage.evaluator.condition import get_condition_common_targets
|
from vitrage.evaluator.condition import get_condition_common_targets
|
||||||
from vitrage.evaluator.condition import parse_condition
|
from vitrage.evaluator.condition import parse_condition
|
||||||
from vitrage.evaluator.condition import SymbolResolver
|
from vitrage.evaluator.condition import SymbolResolver
|
||||||
from vitrage.evaluator.template_data import ActionSpecs
|
|
||||||
from vitrage.evaluator.template_data import EdgeDescription
|
from vitrage.evaluator.template_data import EdgeDescription
|
||||||
from vitrage.evaluator.template_data import ENTITY
|
from vitrage.evaluator.template_data import ENTITY
|
||||||
from vitrage.evaluator.template_data import RELATIONSHIP
|
from vitrage.evaluator.template_data import RELATIONSHIP
|
||||||
from vitrage.evaluator.template_data import Scenario
|
from vitrage.evaluator.template_data import Scenario
|
||||||
from vitrage.evaluator.template_fields import TemplateFields as TFields
|
from vitrage.evaluator.template_fields import TemplateFields as TFields
|
||||||
from vitrage.evaluator.template_loading.subgraph_builder import SubGraphBuilder
|
from vitrage.evaluator.template_loading.subgraph_builder import \
|
||||||
|
SubGraphBuilder
|
||||||
from vitrage.graph import Vertex
|
from vitrage.graph import Vertex
|
||||||
|
|
||||||
|
LOG = log.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
class ScenarioLoader(object):
|
class ScenarioLoader(object):
|
||||||
|
|
||||||
def __init__(self, name, entities, relationships):
|
def __init__(self, template_schema, name, entities, relationships):
|
||||||
self.name = name
|
self.name = name
|
||||||
|
self._template_schema = template_schema
|
||||||
self._template_entities = entities
|
self._template_entities = entities
|
||||||
self._template_relationships = relationships
|
self._template_relationships = relationships
|
||||||
|
|
||||||
@ -93,14 +98,14 @@ class ScenarioLoader(object):
|
|||||||
|
|
||||||
for counter, action_def in enumerate(actions_def):
|
for counter, action_def in enumerate(actions_def):
|
||||||
action_id = '%s-action%s' % (scenario_id, str(counter))
|
action_id = '%s-action%s' % (scenario_id, str(counter))
|
||||||
action_dict = action_def[TFields.ACTION]
|
action_type = action_def[TFields.ACTION][TFields.ACTION_TYPE]
|
||||||
action_type = action_dict[TFields.ACTION_TYPE]
|
action_loader = self._template_schema.loader(action_type)
|
||||||
targets = action_dict.get(TFields.ACTION_TARGET,
|
|
||||||
self.valid_target)
|
|
||||||
properties = action_dict.get(TFields.PROPERTIES, {})
|
|
||||||
|
|
||||||
actions.append(
|
if action_loader:
|
||||||
ActionSpecs(action_id, action_type, targets, properties))
|
actions.append(action_loader.load(action_id, self.valid_target,
|
||||||
|
action_def))
|
||||||
|
else:
|
||||||
|
LOG.warning('Failed to load action of type %s', action_type)
|
||||||
|
|
||||||
return actions
|
return actions
|
||||||
|
|
||||||
|
@ -12,17 +12,23 @@
|
|||||||
# License for the specific language governing permissions and limitations
|
# License for the specific language governing permissions and limitations
|
||||||
# under the License.
|
# under the License.
|
||||||
|
|
||||||
|
from oslo_log import log
|
||||||
|
|
||||||
from vitrage.common.constants import VertexProperties as VProps
|
from vitrage.common.constants import VertexProperties as VProps
|
||||||
from vitrage.evaluator.template_data import EdgeDescription
|
from vitrage.evaluator.template_data import EdgeDescription
|
||||||
from vitrage.evaluator.template_data import TemplateData
|
from vitrage.evaluator.template_data import TemplateData
|
||||||
from vitrage.evaluator.template_fields import TemplateFields as TFields
|
from vitrage.evaluator.template_fields import TemplateFields as TFields
|
||||||
from vitrage.evaluator.template_loading.props_converter import PropsConverter
|
from vitrage.evaluator.template_loading.props_converter import PropsConverter
|
||||||
from vitrage.evaluator.template_loading.scenario_loader import ScenarioLoader
|
from vitrage.evaluator.template_loading.scenario_loader import ScenarioLoader
|
||||||
|
from vitrage.evaluator.template_schema_factory import TemplateSchemaFactory
|
||||||
from vitrage.graph import Edge
|
from vitrage.graph import Edge
|
||||||
from vitrage.graph import Vertex
|
from vitrage.graph import Vertex
|
||||||
from vitrage.utils import evaluator as evaluator_utils
|
from vitrage.utils import evaluator as evaluator_utils
|
||||||
|
|
||||||
|
|
||||||
|
LOG = log.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
class TemplateLoader(object):
|
class TemplateLoader(object):
|
||||||
|
|
||||||
PROPS_CONVERSION = {
|
PROPS_CONVERSION = {
|
||||||
@ -43,6 +49,12 @@ class TemplateLoader(object):
|
|||||||
self.relationships = {}
|
self.relationships = {}
|
||||||
|
|
||||||
def load(self, template_def, def_templates=None):
|
def load(self, template_def, def_templates=None):
|
||||||
|
|
||||||
|
template_schema = self._get_template_schema(template_def)
|
||||||
|
if not template_schema:
|
||||||
|
LOG.error('Failed to load template - unsupported version')
|
||||||
|
return
|
||||||
|
|
||||||
name = template_def[TFields.METADATA][TFields.NAME]
|
name = template_def[TFields.METADATA][TFields.NAME]
|
||||||
|
|
||||||
if def_templates is None:
|
if def_templates is None:
|
||||||
@ -70,8 +82,9 @@ class TemplateLoader(object):
|
|||||||
def_templates,
|
def_templates,
|
||||||
self.relationships)
|
self.relationships)
|
||||||
|
|
||||||
scenarios = ScenarioLoader(name, self.entities, self.relationships)\
|
scenarios = ScenarioLoader(template_schema, name, self.entities,
|
||||||
.build_scenarios(template_def[TFields.SCENARIOS])
|
self.relationships).\
|
||||||
|
build_scenarios(template_def[TFields.SCENARIOS])
|
||||||
|
|
||||||
return TemplateData(name, self.entities, self.relationships, scenarios)
|
return TemplateData(name, self.entities, self.relationships, scenarios)
|
||||||
|
|
||||||
@ -162,3 +175,13 @@ class TemplateLoader(object):
|
|||||||
ignore_ids = [TFields.TEMPLATE_ID, TFields.SOURCE, TFields.TARGET]
|
ignore_ids = [TFields.TEMPLATE_ID, TFields.SOURCE, TFields.TARGET]
|
||||||
return \
|
return \
|
||||||
{key: var_dict[key] for key in var_dict if key not in ignore_ids}
|
{key: var_dict[key] for key in var_dict if key not in ignore_ids}
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _get_template_schema(template):
|
||||||
|
metadata = template.get(TFields.METADATA)
|
||||||
|
|
||||||
|
if metadata:
|
||||||
|
version = metadata.get(TFields.VERSION)
|
||||||
|
return TemplateSchemaFactory().template_schema(version)
|
||||||
|
else:
|
||||||
|
return None
|
||||||
|
15
vitrage/evaluator/template_loading/v1/__init__.py
Normal file
15
vitrage/evaluator/template_loading/v1/__init__.py
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
# Copyright 2017 - Nokia
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||||
|
# not use this file except in compliance with the License. You may obtain
|
||||||
|
# a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
# License for the specific language governing permissions and limitations
|
||||||
|
# under the License.
|
||||||
|
|
||||||
|
__author__ = 'stack'
|
37
vitrage/evaluator/template_loading/v1/action_loader.py
Normal file
37
vitrage/evaluator/template_loading/v1/action_loader.py
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
# Copyright 2016 - 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 abc
|
||||||
|
|
||||||
|
from vitrage.evaluator.template_data import ActionSpecs
|
||||||
|
from vitrage.evaluator.template_fields import TemplateFields as TFields
|
||||||
|
|
||||||
|
|
||||||
|
class BaseActionLoader(object):
|
||||||
|
|
||||||
|
def load(self, action_id, valid_target, action_def):
|
||||||
|
action_dict = action_def[TFields.ACTION]
|
||||||
|
action_type = action_dict[TFields.ACTION_TYPE]
|
||||||
|
targets = action_dict.get(TFields.ACTION_TARGET, valid_target)
|
||||||
|
return ActionSpecs(action_id, action_type, targets,
|
||||||
|
self._get_properties(action_dict))
|
||||||
|
|
||||||
|
@abc.abstractmethod
|
||||||
|
def _get_properties(self, action_dict):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class ActionLoader(BaseActionLoader):
|
||||||
|
def _get_properties(self, action_dict):
|
||||||
|
return action_dict.get(TFields.PROPERTIES, {})
|
@ -0,0 +1,29 @@
|
|||||||
|
# Copyright 2017 - Nokia
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||||
|
# not use this file except in compliance with the License. You may obtain
|
||||||
|
# a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
# License for the specific language governing permissions and limitations
|
||||||
|
# under the License.
|
||||||
|
|
||||||
|
from copy import deepcopy
|
||||||
|
from vitrage.evaluator.actions.recipes.execute_mistral import INPUT
|
||||||
|
from vitrage.evaluator.actions.recipes.execute_mistral import WORKFLOW
|
||||||
|
from vitrage.evaluator.template_fields import TemplateFields as TFields
|
||||||
|
from vitrage.evaluator.template_loading.v1.action_loader import \
|
||||||
|
BaseActionLoader
|
||||||
|
|
||||||
|
|
||||||
|
class ExecuteMistralLoader(BaseActionLoader):
|
||||||
|
def _get_properties(self, action_dict):
|
||||||
|
"""Place all properties under an 'input' block"""
|
||||||
|
properties = action_dict.get(TFields.PROPERTIES, {})
|
||||||
|
input_properties = deepcopy(properties)
|
||||||
|
input_properties.pop(WORKFLOW)
|
||||||
|
return {WORKFLOW: properties[WORKFLOW], INPUT: input_properties}
|
42
vitrage/evaluator/template_schema_factory.py
Normal file
42
vitrage/evaluator/template_schema_factory.py
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
# Copyright 2017 - Nokia
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||||
|
# not use this file except in compliance with the License. You may obtain
|
||||||
|
# a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
# License for the specific language governing permissions and limitations
|
||||||
|
# under the License.
|
||||||
|
from oslo_log import log
|
||||||
|
|
||||||
|
LOG = log.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
class TemplateSchemaFactory(object):
|
||||||
|
|
||||||
|
DEFAULT_VERSION = '1'
|
||||||
|
_schemas = dict()
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def supported_versions(cls):
|
||||||
|
return cls._schemas.keys()
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def is_version_supported(cls, version):
|
||||||
|
return version in cls._schemas
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def template_schema(cls, version):
|
||||||
|
if not version:
|
||||||
|
version = cls.DEFAULT_VERSION
|
||||||
|
template_schema = cls._schemas.get(version)
|
||||||
|
return template_schema
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def register_template_schema(cls, version, template_schema):
|
||||||
|
cls._schemas[version] = template_schema
|
||||||
|
LOG.debug('Registered template schema for version %s', version)
|
86
vitrage/evaluator/template_schemas.py
Normal file
86
vitrage/evaluator/template_schemas.py
Normal file
@ -0,0 +1,86 @@
|
|||||||
|
# Copyright 2017 - Nokia
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||||
|
# not use this file except in compliance with the License. You may obtain
|
||||||
|
# a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
# License for the specific language governing permissions and limitations
|
||||||
|
# under the License.
|
||||||
|
from oslo_log import log
|
||||||
|
|
||||||
|
from vitrage.evaluator.actions.base import ActionType
|
||||||
|
from vitrage.evaluator.template_fields import TemplateFields
|
||||||
|
from vitrage.evaluator.template_loading.v1.action_loader import ActionLoader
|
||||||
|
from vitrage.evaluator.template_loading.v1.execute_mistral_loader import \
|
||||||
|
ExecuteMistralLoader
|
||||||
|
from vitrage.evaluator.template_validation.content.v1.\
|
||||||
|
add_causal_relationship_validator import AddCausalRelationshipValidator
|
||||||
|
from vitrage.evaluator.template_validation.content.v1.definitions_validator \
|
||||||
|
import DefinitionsValidator
|
||||||
|
from vitrage.evaluator.template_validation.content.v1.\
|
||||||
|
execute_mistral_validator import ExecuteMistralValidator as \
|
||||||
|
V1ExecuteMistralValidator
|
||||||
|
from vitrage.evaluator.template_validation.content.v1.mark_down_validator \
|
||||||
|
import MarkDownValidator
|
||||||
|
from vitrage.evaluator.template_validation.content.v1.raise_alarm_validator \
|
||||||
|
import RaiseAlarmValidator
|
||||||
|
from vitrage.evaluator.template_validation.content.v1.scenario_validator \
|
||||||
|
import ScenarioValidator
|
||||||
|
from vitrage.evaluator.template_validation.content.v1.set_state_validator \
|
||||||
|
import SetStateValidator
|
||||||
|
from vitrage.evaluator.template_validation.content.v2.\
|
||||||
|
execute_mistral_validator import ExecuteMistralValidator as \
|
||||||
|
V2ExecuteMistralValidator
|
||||||
|
from vitrage.evaluator.template_schema_factory import TemplateSchemaFactory
|
||||||
|
|
||||||
|
LOG = log.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
class TemplateSchema1(object):
|
||||||
|
def __init__(self):
|
||||||
|
self._validators = {
|
||||||
|
TemplateFields.DEFINITIONS: DefinitionsValidator,
|
||||||
|
TemplateFields.SCENARIOS: ScenarioValidator,
|
||||||
|
ActionType.ADD_CAUSAL_RELATIONSHIP: AddCausalRelationshipValidator,
|
||||||
|
ActionType.EXECUTE_MISTRAL: V1ExecuteMistralValidator,
|
||||||
|
ActionType.MARK_DOWN: MarkDownValidator,
|
||||||
|
ActionType.RAISE_ALARM: RaiseAlarmValidator,
|
||||||
|
ActionType.SET_STATE: SetStateValidator,
|
||||||
|
}
|
||||||
|
|
||||||
|
self._loaders = {
|
||||||
|
ActionType.ADD_CAUSAL_RELATIONSHIP: ActionLoader(),
|
||||||
|
ActionType.EXECUTE_MISTRAL: ExecuteMistralLoader(),
|
||||||
|
ActionType.MARK_DOWN: ActionLoader(),
|
||||||
|
ActionType.RAISE_ALARM: ActionLoader(),
|
||||||
|
ActionType.SET_STATE: ActionLoader(),
|
||||||
|
}
|
||||||
|
|
||||||
|
def validator(self, validator_type):
|
||||||
|
LOG.debug('Get validator. validator_type: %s. validators: %s',
|
||||||
|
validator_type, self._validators)
|
||||||
|
return self._validators.get(validator_type)
|
||||||
|
|
||||||
|
def loader(self, loader_type):
|
||||||
|
LOG.debug('Get loader. loader_type: %s. loaders: %s',
|
||||||
|
loader_type, self._loaders)
|
||||||
|
return self._loaders.get(loader_type)
|
||||||
|
|
||||||
|
|
||||||
|
class TemplateSchema2(TemplateSchema1):
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
super(TemplateSchema2, self).__init__()
|
||||||
|
self._validators[ActionType.EXECUTE_MISTRAL] = \
|
||||||
|
V2ExecuteMistralValidator()
|
||||||
|
self._loaders[ActionType.EXECUTE_MISTRAL] = ActionLoader()
|
||||||
|
|
||||||
|
|
||||||
|
def init_template_schemas():
|
||||||
|
TemplateSchemaFactory.register_template_schema('1', TemplateSchema1())
|
||||||
|
TemplateSchemaFactory.register_template_schema('2', TemplateSchema2())
|
@ -16,6 +16,8 @@ import abc
|
|||||||
import six
|
import six
|
||||||
|
|
||||||
from oslo_log import log
|
from oslo_log import log
|
||||||
|
from vitrage.evaluator.template_fields import TemplateFields
|
||||||
|
from vitrage.evaluator.template_schema_factory import TemplateSchemaFactory
|
||||||
from vitrage.evaluator.template_validation.base import get_correct_result
|
from vitrage.evaluator.template_validation.base import get_correct_result
|
||||||
from vitrage.evaluator.template_validation.base import get_fault_result
|
from vitrage.evaluator.template_validation.base import get_fault_result
|
||||||
from vitrage.evaluator.template_validation.status_messages import status_msgs
|
from vitrage.evaluator.template_validation.status_messages import status_msgs
|
||||||
@ -42,6 +44,23 @@ def validate_template_id(definitions_index, id_to_check):
|
|||||||
return get_correct_result(RESULT_DESCRIPTION)
|
return get_correct_result(RESULT_DESCRIPTION)
|
||||||
|
|
||||||
|
|
||||||
|
def get_template_schema(template):
|
||||||
|
metadata = template.get(TemplateFields.METADATA)
|
||||||
|
|
||||||
|
if metadata is None:
|
||||||
|
LOG.error('%s status code: %s' % (status_msgs[62], 62))
|
||||||
|
return get_content_fault_result(62), None
|
||||||
|
|
||||||
|
version = metadata.get(TemplateFields.VERSION)
|
||||||
|
template_schema = TemplateSchemaFactory().template_schema(version)
|
||||||
|
|
||||||
|
if template_schema:
|
||||||
|
return get_content_correct_result(), template_schema
|
||||||
|
else:
|
||||||
|
LOG.error('%s status code: %s' % (status_msgs[63], 63))
|
||||||
|
return get_content_fault_result(63), None
|
||||||
|
|
||||||
|
|
||||||
@six.add_metaclass(abc.ABCMeta)
|
@six.add_metaclass(abc.ABCMeta)
|
||||||
class ActionValidator(object):
|
class ActionValidator(object):
|
||||||
|
|
||||||
|
@ -14,18 +14,9 @@
|
|||||||
|
|
||||||
from oslo_log import log
|
from oslo_log import log
|
||||||
|
|
||||||
from vitrage.evaluator.template_fields import DEFAULT_VERSION
|
|
||||||
from vitrage.evaluator.template_fields import SUPPORTED_VERSIONS
|
|
||||||
from vitrage.evaluator.template_fields import TemplateFields
|
from vitrage.evaluator.template_fields import TemplateFields
|
||||||
from vitrage.evaluator.template_validation.content.base import \
|
from vitrage.evaluator.template_validation.content.base import \
|
||||||
get_content_correct_result
|
get_template_schema
|
||||||
from vitrage.evaluator.template_validation.content.base import \
|
|
||||||
get_content_fault_result
|
|
||||||
from vitrage.evaluator.template_validation.content.definitions_validator \
|
|
||||||
import DefinitionsValidator as DefValidator
|
|
||||||
from vitrage.evaluator.template_validation.content.scenario_validator import \
|
|
||||||
ScenarioValidator
|
|
||||||
from vitrage.evaluator.template_validation.status_messages import status_msgs
|
|
||||||
|
|
||||||
LOG = log.getLogger(__name__)
|
LOG = log.getLogger(__name__)
|
||||||
|
|
||||||
@ -35,65 +26,57 @@ def content_validation(template, def_templates=None):
|
|||||||
if def_templates is None:
|
if def_templates is None:
|
||||||
def_templates = {}
|
def_templates = {}
|
||||||
|
|
||||||
result = _validate_version(template)
|
|
||||||
|
|
||||||
entities_index = {}
|
entities_index = {}
|
||||||
template_definitions = {}
|
template_definitions = {}
|
||||||
|
|
||||||
|
result, template_schema = get_template_schema(template)
|
||||||
|
def_validator = template_schema.validator(TemplateFields.DEFINITIONS) \
|
||||||
|
if result.is_valid_config and template_schema else None
|
||||||
|
|
||||||
|
if result.is_valid_config and not def_validator:
|
||||||
|
result.is_valid_config = False # Not supposed to happen
|
||||||
|
|
||||||
if result.is_valid_config and TemplateFields.DEFINITIONS in template:
|
if result.is_valid_config and TemplateFields.DEFINITIONS in template:
|
||||||
template_definitions = template[TemplateFields.DEFINITIONS]
|
template_definitions = template[TemplateFields.DEFINITIONS]
|
||||||
|
|
||||||
if TemplateFields.ENTITIES in template_definitions:
|
if TemplateFields.ENTITIES in template_definitions:
|
||||||
entities = template_definitions[TemplateFields.ENTITIES]
|
entities = template_definitions[TemplateFields.ENTITIES]
|
||||||
result = DefValidator.validate_entities_definition(entities,
|
result = def_validator.validate_entities_definition(entities,
|
||||||
entities_index)
|
entities_index)
|
||||||
|
|
||||||
# If there are duplicate definitions in several includes under the same
|
# If there are duplicate definitions in several includes under the same
|
||||||
# name, will regard the first one
|
# name, will regard the first one
|
||||||
if result.is_valid_config and TemplateFields.INCLUDES in template:
|
if result.is_valid_config and TemplateFields.INCLUDES in template:
|
||||||
template_includes = template[TemplateFields.INCLUDES]
|
template_includes = template[TemplateFields.INCLUDES]
|
||||||
result = \
|
result = \
|
||||||
DefValidator.validate_definitions_with_includes(template_includes,
|
def_validator.validate_definitions_with_includes(template_includes,
|
||||||
def_templates,
|
def_templates,
|
||||||
entities_index)
|
entities_index)
|
||||||
|
|
||||||
relationship_index = {}
|
relationship_index = {}
|
||||||
|
|
||||||
if result.is_valid_config and \
|
if result.is_valid_config and \
|
||||||
TemplateFields.RELATIONSHIPS in template_definitions:
|
TemplateFields.RELATIONSHIPS in template_definitions:
|
||||||
relationships = template_definitions[TemplateFields.RELATIONSHIPS]
|
relationships = template_definitions[TemplateFields.RELATIONSHIPS]
|
||||||
result = \
|
result = def_validator.validate_relationships_definitions(
|
||||||
DefValidator.validate_relationships_definitions(relationships,
|
relationships, relationship_index, entities_index)
|
||||||
relationship_index,
|
|
||||||
entities_index)
|
|
||||||
|
|
||||||
if result.is_valid_config and TemplateFields.INCLUDES in template:
|
if result.is_valid_config and TemplateFields.INCLUDES in template:
|
||||||
template_includes = template[TemplateFields.INCLUDES]
|
template_includes = template[TemplateFields.INCLUDES]
|
||||||
result = DefValidator.validate_relationships_definitions_with_includes(
|
result = \
|
||||||
template_includes,
|
def_validator.validate_relationships_definitions_with_includes(
|
||||||
def_templates,
|
template_includes,
|
||||||
entities_index,
|
def_templates,
|
||||||
relationship_index)
|
entities_index,
|
||||||
|
relationship_index)
|
||||||
|
|
||||||
if result.is_valid_config:
|
if result.is_valid_config:
|
||||||
|
scenario_validator = template_schema.validator(
|
||||||
|
TemplateFields.SCENARIOS)
|
||||||
scenarios = template[TemplateFields.SCENARIOS]
|
scenarios = template[TemplateFields.SCENARIOS]
|
||||||
definitions_index = entities_index.copy()
|
definitions_index = entities_index.copy()
|
||||||
definitions_index.update(relationship_index)
|
definitions_index.update(relationship_index)
|
||||||
result = ScenarioValidator(definitions_index).validate(scenarios)
|
result = scenario_validator.validate(template_schema,
|
||||||
|
definitions_index, scenarios)
|
||||||
|
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
|
||||||
def _validate_version(template):
|
|
||||||
metadata = template.get(TemplateFields.METADATA)
|
|
||||||
|
|
||||||
if metadata is None:
|
|
||||||
LOG.error('%s status code: %s' % (status_msgs[62], 62))
|
|
||||||
return get_content_fault_result(62)
|
|
||||||
|
|
||||||
version = metadata.get(TemplateFields.VERSION, DEFAULT_VERSION)
|
|
||||||
if version in SUPPORTED_VERSIONS:
|
|
||||||
return get_content_correct_result()
|
|
||||||
else:
|
|
||||||
LOG.error('%s status code: %s' % (status_msgs[63], 63))
|
|
||||||
return get_content_fault_result(63)
|
|
||||||
|
15
vitrage/evaluator/template_validation/content/v1/__init__.py
Normal file
15
vitrage/evaluator/template_validation/content/v1/__init__.py
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
# Copyright 2017 - Nokia
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||||
|
# not use this file except in compliance with the License. You may obtain
|
||||||
|
# a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
# License for the specific language governing permissions and limitations
|
||||||
|
# under the License.
|
||||||
|
|
||||||
|
__author__ = 'stack'
|
@ -143,6 +143,7 @@ class DefinitionsValidator(object):
|
|||||||
|
|
||||||
return get_content_correct_result()
|
return get_content_correct_result()
|
||||||
|
|
||||||
|
# noinspection PyBroadException
|
||||||
@classmethod
|
@classmethod
|
||||||
def _validate_entity_definition(cls, entity_dict, entities_index):
|
def _validate_entity_definition(cls, entity_dict, entities_index):
|
||||||
template_id = entity_dict[TemplateFields.TEMPLATE_ID]
|
template_id = entity_dict[TemplateFields.TEMPLATE_ID]
|
@ -19,29 +19,18 @@ from oslo_log import log
|
|||||||
from six.moves import reduce
|
from six.moves import reduce
|
||||||
|
|
||||||
from vitrage.common.constants import EdgeProperties as EProps
|
from vitrage.common.constants import EdgeProperties as EProps
|
||||||
from vitrage.evaluator.actions.base import ActionType
|
|
||||||
from vitrage.evaluator.condition import convert_to_dnf_format
|
from vitrage.evaluator.condition import convert_to_dnf_format
|
||||||
from vitrage.evaluator.condition import get_condition_common_targets
|
from vitrage.evaluator.condition import get_condition_common_targets
|
||||||
from vitrage.evaluator.condition import is_condition_include_positive_clause
|
from vitrage.evaluator.condition import is_condition_include_positive_clause
|
||||||
from vitrage.evaluator.condition import parse_condition
|
from vitrage.evaluator.condition import parse_condition
|
||||||
from vitrage.evaluator.condition import SymbolResolver
|
from vitrage.evaluator.condition import SymbolResolver
|
||||||
from vitrage.evaluator.template_fields import TemplateFields
|
from vitrage.evaluator.template_fields import TemplateFields
|
||||||
from vitrage.evaluator.template_validation.content. \
|
|
||||||
add_causal_relationship_validator import AddCausalRelationshipValidator
|
|
||||||
from vitrage.evaluator.template_validation.content.base import \
|
from vitrage.evaluator.template_validation.content.base import \
|
||||||
get_content_correct_result
|
get_content_correct_result
|
||||||
from vitrage.evaluator.template_validation.content.base import \
|
from vitrage.evaluator.template_validation.content.base import \
|
||||||
get_content_fault_result
|
get_content_fault_result
|
||||||
from vitrage.evaluator.template_validation.content.base import \
|
from vitrage.evaluator.template_validation.content.base import \
|
||||||
validate_template_id
|
validate_template_id
|
||||||
from vitrage.evaluator.template_validation.content.execute_mistral_validator \
|
|
||||||
import ExecuteMistralValidator
|
|
||||||
from vitrage.evaluator.template_validation.content.mark_down_validator \
|
|
||||||
import MarkDownValidator
|
|
||||||
from vitrage.evaluator.template_validation.content.raise_alarm_validator \
|
|
||||||
import RaiseAlarmValidator
|
|
||||||
from vitrage.evaluator.template_validation.content.set_state_validator \
|
|
||||||
import SetStateValidator
|
|
||||||
from vitrage.evaluator.template_validation.status_messages import status_msgs
|
from vitrage.evaluator.template_validation.status_messages import status_msgs
|
||||||
|
|
||||||
LOG = log.getLogger(__name__)
|
LOG = log.getLogger(__name__)
|
||||||
@ -62,28 +51,29 @@ class ScenarioValidator(object):
|
|||||||
def get_entity_id(self, entity):
|
def get_entity_id(self, entity):
|
||||||
return entity[TemplateFields.TEMPLATE_ID]
|
return entity[TemplateFields.TEMPLATE_ID]
|
||||||
|
|
||||||
def __init__(self, definitions_index):
|
@classmethod
|
||||||
self.definitions_index = definitions_index
|
def validate(cls, template_schema, def_index, scenarios):
|
||||||
|
|
||||||
def validate(self, scenarios):
|
|
||||||
for scenario in scenarios:
|
for scenario in scenarios:
|
||||||
scenario_values = scenario[TemplateFields.SCENARIO]
|
scenario_values = scenario[TemplateFields.SCENARIO]
|
||||||
|
|
||||||
condition = scenario_values[TemplateFields.CONDITION]
|
condition = scenario_values[TemplateFields.CONDITION]
|
||||||
result = self._validate_scenario_condition(condition)
|
result = cls._validate_scenario_condition(def_index, condition)
|
||||||
|
|
||||||
if not result.is_valid_config:
|
if not result.is_valid_config:
|
||||||
return result
|
return result
|
||||||
|
|
||||||
actions = scenario_values[TemplateFields.ACTIONS]
|
actions = scenario_values[TemplateFields.ACTIONS]
|
||||||
result = self._validate_scenario_actions(actions)
|
result = cls._validate_scenario_actions(template_schema,
|
||||||
|
def_index, actions)
|
||||||
|
|
||||||
if not result.is_valid_config:
|
if not result.is_valid_config:
|
||||||
return result
|
return result
|
||||||
|
|
||||||
return get_content_correct_result()
|
return get_content_correct_result()
|
||||||
|
|
||||||
def _validate_scenario_condition(self, condition):
|
# noinspection PyBroadException
|
||||||
|
@classmethod
|
||||||
|
def _validate_scenario_condition(cls, def_index, condition):
|
||||||
try:
|
try:
|
||||||
dnf_result = convert_to_dnf_format(condition)
|
dnf_result = convert_to_dnf_format(condition)
|
||||||
except Exception:
|
except Exception:
|
||||||
@ -91,7 +81,8 @@ class ScenarioValidator(object):
|
|||||||
return get_content_fault_result(85)
|
return get_content_fault_result(85)
|
||||||
|
|
||||||
# not condition validation
|
# not condition validation
|
||||||
not_condition_result = self._validate_not_condition(dnf_result)
|
not_condition_result = cls._validate_not_condition(def_index,
|
||||||
|
dnf_result)
|
||||||
if not not_condition_result.is_valid_config:
|
if not not_condition_result.is_valid_config:
|
||||||
return not_condition_result
|
return not_condition_result
|
||||||
|
|
||||||
@ -107,28 +98,29 @@ class ScenarioValidator(object):
|
|||||||
continue
|
continue
|
||||||
|
|
||||||
result = \
|
result = \
|
||||||
validate_template_id(self.definitions_index, condition_var)
|
validate_template_id(def_index, condition_var)
|
||||||
if not result.is_valid_config:
|
if not result.is_valid_config:
|
||||||
return result
|
return result
|
||||||
|
|
||||||
# condition structure validation
|
# condition structure validation
|
||||||
condition_structure_result = \
|
condition_structure_result = cls._validate_condition_structure(
|
||||||
self._validate_condition_structure(parse_condition(condition))
|
def_index, parse_condition(condition))
|
||||||
if not condition_structure_result.is_valid_config:
|
if not condition_structure_result.is_valid_config:
|
||||||
return condition_structure_result
|
return condition_structure_result
|
||||||
|
|
||||||
return get_content_correct_result()
|
return get_content_correct_result()
|
||||||
|
|
||||||
def _validate_condition_structure(self, condition_dnf):
|
@classmethod
|
||||||
|
def _validate_condition_structure(cls, def_index, condition_dnf):
|
||||||
result = \
|
result = \
|
||||||
self._validate_condition_includes_positive_clause(condition_dnf)
|
cls._validate_condition_includes_positive_clause(condition_dnf)
|
||||||
if not result.is_valid_config:
|
if not result.is_valid_config:
|
||||||
return result
|
return result
|
||||||
|
|
||||||
common_targets = \
|
common_targets = \
|
||||||
get_condition_common_targets(condition_dnf,
|
get_condition_common_targets(condition_dnf,
|
||||||
self.definitions_index,
|
def_index,
|
||||||
self.TemplateSymbolResolver())
|
cls.TemplateSymbolResolver())
|
||||||
|
|
||||||
return get_content_correct_result() if common_targets \
|
return get_content_correct_result() if common_targets \
|
||||||
else get_content_fault_result(135)
|
else get_content_fault_result(135)
|
||||||
@ -139,65 +131,63 @@ class ScenarioValidator(object):
|
|||||||
is_condition_include_positive_clause(condition) \
|
is_condition_include_positive_clause(condition) \
|
||||||
else get_content_fault_result(134)
|
else get_content_fault_result(134)
|
||||||
|
|
||||||
def _validate_not_condition(self, dnf_result):
|
@classmethod
|
||||||
|
def _validate_not_condition(cls, def_index, dnf_result):
|
||||||
"""Not operator validation
|
"""Not operator validation
|
||||||
|
|
||||||
Not operator can appear only on edges.
|
Not operator can appear only on edges.
|
||||||
|
|
||||||
:param dnf_result:
|
:param dnf_result:
|
||||||
:param definitions_index:
|
:param def_index:
|
||||||
:return:
|
:return:
|
||||||
"""
|
"""
|
||||||
|
|
||||||
if isinstance(dnf_result, Not):
|
if isinstance(dnf_result, Not):
|
||||||
for arg in dnf_result.args:
|
for arg in dnf_result.args:
|
||||||
if isinstance(arg, Symbol):
|
if isinstance(arg, Symbol):
|
||||||
definition = self.definitions_index.get(str(arg), None)
|
definition = def_index.get(str(arg), None)
|
||||||
if not (definition and
|
if not (definition and
|
||||||
definition.get(EProps.RELATIONSHIP_TYPE)):
|
definition.get(EProps.RELATIONSHIP_TYPE)):
|
||||||
msg = status_msgs[86] + ' template id: %s' % arg
|
msg = status_msgs[86] + ' template id: %s' % arg
|
||||||
LOG.error('%s status code: %s' % (msg, 86))
|
LOG.error('%s status code: %s' % (msg, 86))
|
||||||
return get_content_fault_result(86, msg)
|
return get_content_fault_result(86, msg)
|
||||||
else:
|
else:
|
||||||
res = self._validate_not_condition(arg)
|
res = cls._validate_not_condition(def_index, arg)
|
||||||
if not res.is_valid_config:
|
if not res.is_valid_config:
|
||||||
return res
|
return res
|
||||||
return get_content_correct_result()
|
return get_content_correct_result()
|
||||||
|
|
||||||
for arg in dnf_result.args:
|
for arg in dnf_result.args:
|
||||||
if not isinstance(arg, Symbol):
|
if not isinstance(arg, Symbol):
|
||||||
res = self._validate_not_condition(arg)
|
res = cls._validate_not_condition(def_index, arg)
|
||||||
if not res.is_valid_config:
|
if not res.is_valid_config:
|
||||||
return res
|
return res
|
||||||
|
|
||||||
return get_content_correct_result()
|
return get_content_correct_result()
|
||||||
|
|
||||||
def _validate_scenario_actions(self, actions):
|
@classmethod
|
||||||
|
def _validate_scenario_actions(cls,
|
||||||
|
template_schema,
|
||||||
|
def_index,
|
||||||
|
actions):
|
||||||
|
|
||||||
for action in actions:
|
for action in actions:
|
||||||
result = \
|
result = \
|
||||||
self._validate_scenario_action(action[TemplateFields.ACTION])
|
cls._validate_scenario_action(template_schema,
|
||||||
|
def_index,
|
||||||
|
action[TemplateFields.ACTION])
|
||||||
if not result.is_valid_config:
|
if not result.is_valid_config:
|
||||||
return result
|
return result
|
||||||
|
|
||||||
return get_content_correct_result()
|
return get_content_correct_result()
|
||||||
|
|
||||||
def _validate_scenario_action(self, action):
|
@staticmethod
|
||||||
|
def _validate_scenario_action(template_schema, def_index, action):
|
||||||
action_type = action[TemplateFields.ACTION_TYPE]
|
action_type = action[TemplateFields.ACTION_TYPE]
|
||||||
|
action_validator = template_schema.validator(action_type)
|
||||||
|
|
||||||
action_validators = {
|
if not action_validator:
|
||||||
ActionType.RAISE_ALARM: RaiseAlarmValidator(),
|
|
||||||
ActionType.SET_STATE: SetStateValidator(),
|
|
||||||
ActionType.ADD_CAUSAL_RELATIONSHIP:
|
|
||||||
AddCausalRelationshipValidator(),
|
|
||||||
ActionType.MARK_DOWN: MarkDownValidator(),
|
|
||||||
ActionType.EXECUTE_MISTRAL: ExecuteMistralValidator(),
|
|
||||||
}
|
|
||||||
|
|
||||||
if action_type not in action_validators:
|
|
||||||
LOG.error('%s status code: %s' % (status_msgs[120], 120))
|
LOG.error('%s status code: %s' % (status_msgs[120], 120))
|
||||||
return get_content_fault_result(120)
|
return get_content_fault_result(120)
|
||||||
|
|
||||||
return action_validators[action_type].validate(action,
|
return action_validator.validate(action, def_index)
|
||||||
self.definitions_index)
|
|
15
vitrage/evaluator/template_validation/content/v2/__init__.py
Normal file
15
vitrage/evaluator/template_validation/content/v2/__init__.py
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
# Copyright 2017 - Nokia
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||||
|
# not use this file except in compliance with the License. You may obtain
|
||||||
|
# a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
# License for the specific language governing permissions and limitations
|
||||||
|
# under the License.
|
||||||
|
|
||||||
|
__author__ = 'stack'
|
@ -0,0 +1,47 @@
|
|||||||
|
# Copyright 2017 - Nokia
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||||
|
# not use this file except in compliance with the License. You may obtain
|
||||||
|
# a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
# License for the specific language governing permissions and limitations
|
||||||
|
# under the License.
|
||||||
|
|
||||||
|
from oslo_log import log
|
||||||
|
|
||||||
|
from vitrage.evaluator.actions.recipes.execute_mistral import INPUT
|
||||||
|
from vitrage.evaluator.actions.recipes.execute_mistral import WORKFLOW
|
||||||
|
from vitrage.evaluator.template_fields import TemplateFields
|
||||||
|
from vitrage.evaluator.template_validation.content.base import \
|
||||||
|
ActionValidator
|
||||||
|
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__)
|
||||||
|
|
||||||
|
|
||||||
|
class ExecuteMistralValidator(ActionValidator):
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def validate(action, definitions_index):
|
||||||
|
properties = action[TemplateFields.PROPERTIES]
|
||||||
|
|
||||||
|
if WORKFLOW not in properties or not properties[WORKFLOW]:
|
||||||
|
LOG.error('%s status code: %s' % (status_msgs[133], 133))
|
||||||
|
return get_content_fault_result(133)
|
||||||
|
|
||||||
|
for prop in properties:
|
||||||
|
if prop not in {WORKFLOW, INPUT}:
|
||||||
|
LOG.error('%s status code: %s' % (status_msgs[136], 136))
|
||||||
|
return get_content_fault_result(136)
|
||||||
|
|
||||||
|
return get_content_correct_result()
|
@ -14,7 +14,8 @@
|
|||||||
from vitrage.common.constants import EdgeLabel
|
from vitrage.common.constants import EdgeLabel
|
||||||
from vitrage.common.constants import EntityCategory
|
from vitrage.common.constants import EntityCategory
|
||||||
from vitrage.evaluator.actions.base import action_types
|
from vitrage.evaluator.actions.base import action_types
|
||||||
from vitrage.evaluator.template_fields import SUPPORTED_VERSIONS
|
from vitrage.evaluator.template_schema_factory import TemplateSchemaFactory
|
||||||
|
|
||||||
|
|
||||||
status_msgs = {
|
status_msgs = {
|
||||||
|
|
||||||
@ -43,7 +44,7 @@ status_msgs = {
|
|||||||
60: 'metadata section must contain id field.',
|
60: 'metadata section must contain id field.',
|
||||||
62: 'metadata is a mandatory section.',
|
62: 'metadata is a mandatory section.',
|
||||||
63: 'Unsupported version. Version must be one of: {versions}'
|
63: 'Unsupported version. Version must be one of: {versions}'
|
||||||
.format(versions=SUPPORTED_VERSIONS),
|
.format(versions=TemplateSchemaFactory.supported_versions()),
|
||||||
|
|
||||||
# scenarios section status messages 80-99
|
# scenarios section status messages 80-99
|
||||||
80: 'scenarios is a mandatory section.',
|
80: 'scenarios is a mandatory section.',
|
||||||
@ -84,6 +85,8 @@ status_msgs = {
|
|||||||
'block',
|
'block',
|
||||||
134: 'condition can not contain only \'not\' clauses',
|
134: 'condition can not contain only \'not\' clauses',
|
||||||
135: 'condition must contain a common entity for all \'or\' clauses',
|
135: 'condition must contain a common entity for all \'or\' clauses',
|
||||||
|
136: 'Input parameters for the Mistral workflow in execute_mistral action '
|
||||||
|
'must be placed under an \'input\' block ',
|
||||||
|
|
||||||
# def_templates status messages 140-159
|
# def_templates status messages 140-159
|
||||||
140: 'At least one template must be included',
|
140: 'At least one template must be included',
|
||||||
|
@ -14,6 +14,7 @@
|
|||||||
from oslo_log import log as logging
|
from oslo_log import log as logging
|
||||||
|
|
||||||
from vitrage.common.constants import NotifierEventTypes
|
from vitrage.common.constants import NotifierEventTypes
|
||||||
|
from vitrage.evaluator.actions.recipes.execute_mistral import INPUT
|
||||||
from vitrage.evaluator.actions.recipes.execute_mistral import WORKFLOW
|
from vitrage.evaluator.actions.recipes.execute_mistral import WORKFLOW
|
||||||
from vitrage.notifier.plugins.base import NotifierBase
|
from vitrage.notifier.plugins.base import NotifierBase
|
||||||
from vitrage import os_clients
|
from vitrage import os_clients
|
||||||
@ -63,11 +64,11 @@ class MistralNotifier(NotifierBase):
|
|||||||
|
|
||||||
try:
|
try:
|
||||||
workflow = data[WORKFLOW]
|
workflow = data[WORKFLOW]
|
||||||
del data[WORKFLOW]
|
workflow_input = data.get(INPUT, {})
|
||||||
|
|
||||||
response = self.client.executions.create(
|
response = self.client.executions.create(
|
||||||
workflow_identifier=workflow,
|
workflow_identifier=workflow,
|
||||||
workflow_input=data,
|
workflow_input=workflow_input,
|
||||||
wf_params={})
|
wf_params={})
|
||||||
|
|
||||||
if response:
|
if response:
|
||||||
|
@ -1,25 +1,26 @@
|
|||||||
metadata:
|
metadata:
|
||||||
name: execute_mistral
|
version: 1
|
||||||
|
name: v1_execute_mistral
|
||||||
description: execute mistral
|
description: execute mistral
|
||||||
definitions:
|
definitions:
|
||||||
entities:
|
entities:
|
||||||
- entity:
|
- entity:
|
||||||
category: ALARM
|
category: ALARM
|
||||||
name: compute.host.down
|
name: notifiers.mistral.trigger.alarm.1
|
||||||
template_id: host_down_alarm
|
template_id: alarm
|
||||||
- entity:
|
- entity:
|
||||||
category: RESOURCE
|
category: RESOURCE
|
||||||
type: nova.host
|
type: nova.host
|
||||||
template_id: host
|
template_id: host
|
||||||
relationships:
|
relationships:
|
||||||
- relationship:
|
- relationship:
|
||||||
source: host_down_alarm
|
source: alarm
|
||||||
relationship_type: on
|
relationship_type: on
|
||||||
target: host
|
target: host
|
||||||
template_id : host_down_alarm_on_host
|
template_id : alarm_on_host
|
||||||
scenarios:
|
scenarios:
|
||||||
- scenario:
|
- scenario:
|
||||||
condition: host_down_alarm_on_host
|
condition: alarm_on_host
|
||||||
actions:
|
actions:
|
||||||
- action:
|
- action:
|
||||||
action_type: execute_mistral
|
action_type: execute_mistral
|
@ -0,0 +1,30 @@
|
|||||||
|
metadata:
|
||||||
|
version: 2
|
||||||
|
name: v2_execute_mistral
|
||||||
|
description: execute mistral
|
||||||
|
definitions:
|
||||||
|
entities:
|
||||||
|
- entity:
|
||||||
|
category: ALARM
|
||||||
|
name: notifiers.mistral.trigger.alarm.2
|
||||||
|
template_id: alarm
|
||||||
|
- entity:
|
||||||
|
category: RESOURCE
|
||||||
|
type: nova.host
|
||||||
|
template_id: host
|
||||||
|
relationships:
|
||||||
|
- relationship:
|
||||||
|
source: alarm
|
||||||
|
relationship_type: on
|
||||||
|
target: host
|
||||||
|
template_id : alarm_on_host
|
||||||
|
scenarios:
|
||||||
|
- scenario:
|
||||||
|
condition: alarm_on_host
|
||||||
|
actions:
|
||||||
|
- action:
|
||||||
|
action_type: execute_mistral
|
||||||
|
properties:
|
||||||
|
workflow: wf_for_tempest_test_1234
|
||||||
|
input:
|
||||||
|
farewell: Hello and Goodbye
|
@ -11,28 +11,30 @@
|
|||||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
# License for the specific language governing permissions and limitations
|
# License for the specific language governing permissions and limitations
|
||||||
# under the License.
|
# under the License.
|
||||||
|
import abc
|
||||||
|
|
||||||
from vitrage.evaluator.actions.base import ActionType
|
from vitrage.evaluator.actions.base import ActionType
|
||||||
from vitrage.evaluator.actions.recipes.execute_mistral import WORKFLOW
|
from vitrage.evaluator.actions.recipes.execute_mistral import WORKFLOW
|
||||||
from vitrage.evaluator.template_fields import TemplateFields
|
from vitrage.evaluator.template_fields import TemplateFields
|
||||||
from vitrage.evaluator.template_validation.content.execute_mistral_validator \
|
|
||||||
import ExecuteMistralValidator
|
|
||||||
from vitrage.tests.unit.evaluator.template_validation.content.base import \
|
from vitrage.tests.unit.evaluator.template_validation.content.base import \
|
||||||
ActionValidatorTest
|
ActionValidatorTest
|
||||||
from vitrage.tests.unit.evaluator.template_validation.content.base import \
|
from vitrage.tests.unit.evaluator.template_validation.content.base import \
|
||||||
DEFINITIONS_INDEX_MOCK
|
DEFINITIONS_INDEX_MOCK
|
||||||
|
|
||||||
|
|
||||||
class ExecuteMistralValidatorTest(ActionValidatorTest):
|
class BaseExecuteMistralValidatorTest(ActionValidatorTest):
|
||||||
|
|
||||||
def test_validate_execute_mistral_action(self):
|
@abc.abstractmethod
|
||||||
|
def _create_execute_mistral_action(self, workflow, host, host_state):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def _validate_execute_mistral_action(self, validator):
|
||||||
self._validate_action(
|
self._validate_action(
|
||||||
self._create_execute_mistral_action('wf_1', 'host_2', 'down'),
|
self._create_execute_mistral_action('wf_1', 'host_2', 'down'),
|
||||||
ExecuteMistralValidator.validate
|
validator.validate
|
||||||
)
|
)
|
||||||
|
|
||||||
def test_validate_execute_mistral_action_without_workflow(self):
|
def _validate_execute_mistral_action_without_workflow(self, validator):
|
||||||
|
|
||||||
# Test setup
|
# Test setup
|
||||||
idx = DEFINITIONS_INDEX_MOCK.copy()
|
idx = DEFINITIONS_INDEX_MOCK.copy()
|
||||||
@ -40,51 +42,63 @@ class ExecuteMistralValidatorTest(ActionValidatorTest):
|
|||||||
action[TemplateFields.PROPERTIES].pop(WORKFLOW)
|
action[TemplateFields.PROPERTIES].pop(WORKFLOW)
|
||||||
|
|
||||||
# Test action
|
# Test action
|
||||||
result = ExecuteMistralValidator.validate(action, idx)
|
result = validator.validate(action, idx)
|
||||||
|
|
||||||
# Test assertions
|
# Test assertions
|
||||||
self._assert_fault_result(result, 133)
|
self._assert_fault_result(result, 133)
|
||||||
|
|
||||||
def test_validate_execute_mistral_action_with_empty_workflow(self):
|
def _validate_execute_mistral_action_with_empty_workflow(self, validator):
|
||||||
|
|
||||||
# Test setup
|
# Test setup
|
||||||
idx = DEFINITIONS_INDEX_MOCK.copy()
|
idx = DEFINITIONS_INDEX_MOCK.copy()
|
||||||
action = self._create_execute_mistral_action('', 'host_2', 'down')
|
action = self._create_execute_mistral_action('', 'host_2', 'down')
|
||||||
|
|
||||||
# Test action
|
# Test action
|
||||||
result = ExecuteMistralValidator.validate(action, idx)
|
result = validator.validate(action, idx)
|
||||||
|
|
||||||
# Test assertions
|
# Test assertions
|
||||||
self._assert_fault_result(result, 133)
|
self._assert_fault_result(result, 133)
|
||||||
|
|
||||||
def test_validate_execute_mistral_action_with_none_workflow(self):
|
def _validate_execute_mistral_action_with_none_workflow(self, validator):
|
||||||
|
|
||||||
# Test setup
|
# Test setup
|
||||||
idx = DEFINITIONS_INDEX_MOCK.copy()
|
idx = DEFINITIONS_INDEX_MOCK.copy()
|
||||||
action = self._create_execute_mistral_action(None, 'host_2', 'down')
|
action = self._create_execute_mistral_action(None, 'host_2', 'down')
|
||||||
|
|
||||||
# Test action
|
# Test action
|
||||||
result = ExecuteMistralValidator.validate(action, idx)
|
result = validator.validate(action, idx)
|
||||||
|
|
||||||
# Test assertions
|
# Test assertions
|
||||||
self._assert_fault_result(result, 133)
|
self._assert_fault_result(result, 133)
|
||||||
|
|
||||||
def test_validate_execute_mistral_action_without_additional_params(self):
|
def _validate_execute_mistral_action_without_additional_props(self,
|
||||||
|
validator):
|
||||||
|
|
||||||
# Test setup - having only the 'workflow' param is a legal config
|
# Test setup - having only the 'workflow' param is a legal config
|
||||||
idx = DEFINITIONS_INDEX_MOCK.copy()
|
idx = DEFINITIONS_INDEX_MOCK.copy()
|
||||||
action = self._create_execute_mistral_action('wf_1', 'host_2', 'down')
|
action = self._create_no_input_mistral_action('wf_1')
|
||||||
action[TemplateFields.PROPERTIES].pop('host')
|
|
||||||
action[TemplateFields.PROPERTIES].pop('host_state')
|
|
||||||
|
|
||||||
# Test action
|
# Test action
|
||||||
result = ExecuteMistralValidator.validate(action, idx)
|
result = validator.validate(action, idx)
|
||||||
|
|
||||||
# Test assertions
|
# Test assertions
|
||||||
self._assert_correct_result(result)
|
self._assert_correct_result(result)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _create_execute_mistral_action(workflow, host, host_state):
|
def _create_no_input_mistral_action(workflow):
|
||||||
|
|
||||||
|
properties = {
|
||||||
|
WORKFLOW: workflow,
|
||||||
|
}
|
||||||
|
action = {
|
||||||
|
TemplateFields.ACTION_TYPE: ActionType.EXECUTE_MISTRAL,
|
||||||
|
TemplateFields.PROPERTIES: properties
|
||||||
|
}
|
||||||
|
|
||||||
|
return action
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _create_v1_execute_mistral_action(workflow, host, host_state):
|
||||||
|
|
||||||
properties = {
|
properties = {
|
||||||
WORKFLOW: workflow,
|
WORKFLOW: workflow,
|
||||||
@ -97,3 +111,21 @@ class ExecuteMistralValidatorTest(ActionValidatorTest):
|
|||||||
}
|
}
|
||||||
|
|
||||||
return action
|
return action
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _create_v2_execute_mistral_action(workflow, host, host_state):
|
||||||
|
|
||||||
|
input_props = {
|
||||||
|
'host': host,
|
||||||
|
'host_state': host_state
|
||||||
|
}
|
||||||
|
properties = {
|
||||||
|
WORKFLOW: workflow,
|
||||||
|
'input': input_props
|
||||||
|
}
|
||||||
|
action = {
|
||||||
|
TemplateFields.ACTION_TYPE: ActionType.EXECUTE_MISTRAL,
|
||||||
|
TemplateFields.PROPERTIES: properties
|
||||||
|
}
|
||||||
|
|
||||||
|
return action
|
@ -0,0 +1,15 @@
|
|||||||
|
# Copyright 2017 - Nokia
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||||
|
# not use this file except in compliance with the License. You may obtain
|
||||||
|
# a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
# License for the specific language governing permissions and limitations
|
||||||
|
# under the License.
|
||||||
|
|
||||||
|
__author__ = 'stack'
|
@ -14,7 +14,7 @@
|
|||||||
|
|
||||||
from vitrage.evaluator.actions.base import ActionType
|
from vitrage.evaluator.actions.base import ActionType
|
||||||
from vitrage.evaluator.template_fields import TemplateFields
|
from vitrage.evaluator.template_fields import TemplateFields
|
||||||
from vitrage.evaluator.template_validation.content. \
|
from vitrage.evaluator.template_validation.content.v1.\
|
||||||
add_causal_relationship_validator import AddCausalRelationshipValidator
|
add_causal_relationship_validator import AddCausalRelationshipValidator
|
||||||
from vitrage.tests.unit.evaluator.template_validation.content.base import \
|
from vitrage.tests.unit.evaluator.template_validation.content.base import \
|
||||||
ActionValidatorTest
|
ActionValidatorTest
|
@ -0,0 +1,65 @@
|
|||||||
|
# Copyright 2017 - Nokia
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||||
|
# not use this file except in compliance with the License. You may obtain
|
||||||
|
# a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
# License for the specific language governing permissions and limitations
|
||||||
|
# under the License.
|
||||||
|
from vitrage.evaluator.template_fields import TemplateFields
|
||||||
|
from vitrage.evaluator.template_validation.content.v1.\
|
||||||
|
execute_mistral_validator import ExecuteMistralValidator as \
|
||||||
|
V1ExecuteMistralValidator
|
||||||
|
from vitrage.tests.unit.evaluator.template_validation.content.\
|
||||||
|
base_test_execute_mistral_validator import BaseExecuteMistralValidatorTest
|
||||||
|
from vitrage.tests.unit.evaluator.template_validation.content.base import \
|
||||||
|
DEFINITIONS_INDEX_MOCK
|
||||||
|
|
||||||
|
|
||||||
|
class ExecuteMistralValidatorTest(BaseExecuteMistralValidatorTest):
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def setUpClass(cls):
|
||||||
|
super(ExecuteMistralValidatorTest, cls).setUpClass()
|
||||||
|
cls.validator = V1ExecuteMistralValidator()
|
||||||
|
|
||||||
|
def test_v1_validate_execute_mistral_action(self):
|
||||||
|
self._validate_execute_mistral_action(self.validator)
|
||||||
|
|
||||||
|
def test_validate_execute_mistral_action_without_workflow(self):
|
||||||
|
self._validate_execute_mistral_action_without_workflow(self.validator)
|
||||||
|
|
||||||
|
def test_validate_execute_mistral_action_with_empty_workflow(self):
|
||||||
|
self._validate_execute_mistral_action_with_empty_workflow(
|
||||||
|
self.validator)
|
||||||
|
|
||||||
|
def test_validate_execute_mistral_action_with_none_workflow(self):
|
||||||
|
self._validate_execute_mistral_action_with_none_workflow(
|
||||||
|
self.validator)
|
||||||
|
|
||||||
|
def test_validate_execute_mistral_action_without_additional_props(self):
|
||||||
|
self._validate_execute_mistral_action_without_additional_props(
|
||||||
|
self.validator)
|
||||||
|
|
||||||
|
def test_validate_execute_mistral_action_with_input_prop(self):
|
||||||
|
"""A version1 execute_mistral action can have an 'input' property"""
|
||||||
|
|
||||||
|
# Test setup
|
||||||
|
idx = DEFINITIONS_INDEX_MOCK.copy()
|
||||||
|
action = self._create_execute_mistral_action('wf_1', 'host_2', 'down')
|
||||||
|
action[TemplateFields.PROPERTIES]['input'] = 'kuku'
|
||||||
|
|
||||||
|
# Test action
|
||||||
|
result = self.validator.validate(action, idx)
|
||||||
|
|
||||||
|
# Test assertions
|
||||||
|
self._assert_correct_result(result)
|
||||||
|
|
||||||
|
def _create_execute_mistral_action(self, workflow, host, host_state):
|
||||||
|
return self.\
|
||||||
|
_create_v1_execute_mistral_action(workflow, host, host_state)
|
@ -14,7 +14,7 @@
|
|||||||
|
|
||||||
from vitrage.evaluator.actions.base import ActionType
|
from vitrage.evaluator.actions.base import ActionType
|
||||||
from vitrage.evaluator.template_fields import TemplateFields
|
from vitrage.evaluator.template_fields import TemplateFields
|
||||||
from vitrage.evaluator.template_validation.content.mark_down_validator import \
|
from vitrage.evaluator.template_validation.content.v1.mark_down_validator import \
|
||||||
MarkDownValidator
|
MarkDownValidator
|
||||||
from vitrage.tests.unit.evaluator.template_validation.content.base import \
|
from vitrage.tests.unit.evaluator.template_validation.content.base import \
|
||||||
ActionValidatorTest
|
ActionValidatorTest
|
@ -14,7 +14,7 @@
|
|||||||
|
|
||||||
from vitrage.evaluator.actions.base import ActionType
|
from vitrage.evaluator.actions.base import ActionType
|
||||||
from vitrage.evaluator.template_fields import TemplateFields
|
from vitrage.evaluator.template_fields import TemplateFields
|
||||||
from vitrage.evaluator.template_validation.content.raise_alarm_validator \
|
from vitrage.evaluator.template_validation.content.v1.raise_alarm_validator \
|
||||||
import RaiseAlarmValidator
|
import RaiseAlarmValidator
|
||||||
from vitrage.tests.unit.evaluator.template_validation.content.base import \
|
from vitrage.tests.unit.evaluator.template_validation.content.base import \
|
||||||
ActionValidatorTest
|
ActionValidatorTest
|
@ -16,7 +16,7 @@ from vitrage.entity_graph.mappings.operational_resource_state import \
|
|||||||
OperationalResourceState
|
OperationalResourceState
|
||||||
from vitrage.evaluator.actions.base import ActionType
|
from vitrage.evaluator.actions.base import ActionType
|
||||||
from vitrage.evaluator.template_fields import TemplateFields
|
from vitrage.evaluator.template_fields import TemplateFields
|
||||||
from vitrage.evaluator.template_validation.content.set_state_validator \
|
from vitrage.evaluator.template_validation.content.v1.set_state_validator \
|
||||||
import SetStateValidator
|
import SetStateValidator
|
||||||
from vitrage.tests.unit.evaluator.template_validation.content.base import \
|
from vitrage.tests.unit.evaluator.template_validation.content.base import \
|
||||||
ActionValidatorTest
|
ActionValidatorTest
|
@ -0,0 +1,15 @@
|
|||||||
|
# Copyright 2017 - Nokia
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||||
|
# not use this file except in compliance with the License. You may obtain
|
||||||
|
# a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
# License for the specific language governing permissions and limitations
|
||||||
|
# under the License.
|
||||||
|
|
||||||
|
__author__ = 'stack'
|
@ -0,0 +1,68 @@
|
|||||||
|
# Copyright 2017 - Nokia
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||||
|
# not use this file except in compliance with the License. You may obtain
|
||||||
|
# a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
# License for the specific language governing permissions and limitations
|
||||||
|
# under the License.
|
||||||
|
from vitrage.evaluator.template_validation.content.v2.\
|
||||||
|
execute_mistral_validator import ExecuteMistralValidator as \
|
||||||
|
V2ExecuteMistralValidator
|
||||||
|
from vitrage.tests.unit.evaluator.template_validation.content.\
|
||||||
|
base_test_execute_mistral_validator import BaseExecuteMistralValidatorTest
|
||||||
|
from vitrage.tests.unit.evaluator.template_validation.content.base import \
|
||||||
|
DEFINITIONS_INDEX_MOCK
|
||||||
|
|
||||||
|
|
||||||
|
class ExecuteMistralValidatorTest(BaseExecuteMistralValidatorTest):
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def setUpClass(cls):
|
||||||
|
super(ExecuteMistralValidatorTest, cls).setUpClass()
|
||||||
|
cls.validator = V2ExecuteMistralValidator()
|
||||||
|
|
||||||
|
def test_v2_validate_execute_mistral_action(self):
|
||||||
|
self._validate_execute_mistral_action(self.validator)
|
||||||
|
|
||||||
|
def test_v2_validate_old_execute_mistral_action(self):
|
||||||
|
"""Test version2 validator on version1 template.
|
||||||
|
|
||||||
|
An execute_mistral action from version 1 should fail in the validation
|
||||||
|
of version 2.
|
||||||
|
"""
|
||||||
|
|
||||||
|
# Test setup
|
||||||
|
idx = DEFINITIONS_INDEX_MOCK.copy()
|
||||||
|
v1_action = \
|
||||||
|
self._create_v1_execute_mistral_action('wf_1', 'host_2', 'down')
|
||||||
|
|
||||||
|
# Test action
|
||||||
|
result = self.validator.validate(v1_action, idx)
|
||||||
|
|
||||||
|
# Test assertions
|
||||||
|
self._assert_fault_result(result, 136)
|
||||||
|
|
||||||
|
def test_validate_execute_mistral_action_without_workflow(self):
|
||||||
|
self._validate_execute_mistral_action_without_workflow(self.validator)
|
||||||
|
|
||||||
|
def test_validate_execute_mistral_action_with_empty_workflow(self):
|
||||||
|
self._validate_execute_mistral_action_with_empty_workflow(
|
||||||
|
self.validator)
|
||||||
|
|
||||||
|
def test_validate_execute_mistral_action_with_none_workflow(self):
|
||||||
|
self._validate_execute_mistral_action_with_none_workflow(
|
||||||
|
self.validator)
|
||||||
|
|
||||||
|
def test_validate_execute_mistral_action_without_additional_props(self):
|
||||||
|
self._validate_execute_mistral_action_without_additional_props(
|
||||||
|
self.validator)
|
||||||
|
|
||||||
|
def _create_execute_mistral_action(self, workflow, host, host_state):
|
||||||
|
return self.\
|
||||||
|
_create_v2_execute_mistral_action(workflow, host, host_state)
|
@ -15,7 +15,7 @@
|
|||||||
from vitrage.evaluator.condition import SymbolResolver
|
from vitrage.evaluator.condition import SymbolResolver
|
||||||
from vitrage.evaluator.template_data import EdgeDescription
|
from vitrage.evaluator.template_data import EdgeDescription
|
||||||
from vitrage.evaluator.template_loading.template_loader import TemplateLoader
|
from vitrage.evaluator.template_loading.template_loader import TemplateLoader
|
||||||
from vitrage.evaluator.template_validation.content.scenario_validator \
|
from vitrage.evaluator.template_validation.content.v1.scenario_validator \
|
||||||
import get_condition_common_targets
|
import get_condition_common_targets
|
||||||
from vitrage.tests import base
|
from vitrage.tests import base
|
||||||
from vitrage.tests.mocks import utils
|
from vitrage.tests.mocks import utils
|
||||||
|
48
vitrage/tests/unit/evaluator/test_scenario_evaluator.py
Normal file
48
vitrage/tests/unit/evaluator/test_scenario_evaluator.py
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
# Copyright 2016 - 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.actions.base import ActionType
|
||||||
|
from vitrage.evaluator.scenario_evaluator import ScenarioEvaluator
|
||||||
|
from vitrage.evaluator.template_data import ActionSpecs
|
||||||
|
from vitrage.tests import base
|
||||||
|
|
||||||
|
|
||||||
|
class TestScenarioEvaluator(base.BaseTest):
|
||||||
|
|
||||||
|
def test_verify_execute_mistral_v2_action_hash(self):
|
||||||
|
execute_mistral_action_spec_1 = \
|
||||||
|
ActionSpecs(id='mistmistmist1',
|
||||||
|
type=ActionType.EXECUTE_MISTRAL,
|
||||||
|
targets={},
|
||||||
|
properties={'workflow': 'wf4',
|
||||||
|
'input': {
|
||||||
|
'prop1': 'ppp',
|
||||||
|
'prop2': 'qqq',
|
||||||
|
'prop3': 'rrr',
|
||||||
|
}})
|
||||||
|
|
||||||
|
execute_mistral_action_spec_2 = \
|
||||||
|
ActionSpecs(id='mistmistmist2',
|
||||||
|
type=ActionType.EXECUTE_MISTRAL,
|
||||||
|
targets={},
|
||||||
|
properties={'workflow': 'wf4',
|
||||||
|
'input': {
|
||||||
|
'prop2': 'qqq',
|
||||||
|
'prop3': 'rrr',
|
||||||
|
'prop1': 'ppp',
|
||||||
|
}})
|
||||||
|
|
||||||
|
self.assertEqual(ScenarioEvaluator.
|
||||||
|
_generate_action_id(execute_mistral_action_spec_1),
|
||||||
|
ScenarioEvaluator.
|
||||||
|
_generate_action_id(execute_mistral_action_spec_2))
|
@ -38,6 +38,8 @@ class BasicTemplateTest(base.BaseTest):
|
|||||||
|
|
||||||
BASIC_TEMPLATE = 'basic.yaml'
|
BASIC_TEMPLATE = 'basic.yaml'
|
||||||
BASIC_TEMPLATE_WITH_INCLUDE = 'basic_with_include.yaml'
|
BASIC_TEMPLATE_WITH_INCLUDE = 'basic_with_include.yaml'
|
||||||
|
V1_MISTRAL_TEMPLATE = 'v1_execute_mistral.yaml'
|
||||||
|
V2_MISTRAL_TEMPLATE = 'v2_execute_mistral.yaml'
|
||||||
DEF_TEMPLATE_TESTS_DIR = utils.get_resources_dir() +\
|
DEF_TEMPLATE_TESTS_DIR = utils.get_resources_dir() +\
|
||||||
'/templates/def_template_tests'
|
'/templates/def_template_tests'
|
||||||
|
|
||||||
@ -230,6 +232,39 @@ class BasicTemplateTest(base.BaseTest):
|
|||||||
expected_relationships,
|
expected_relationships,
|
||||||
expected_scenario)
|
expected_scenario)
|
||||||
|
|
||||||
|
def test_convert_v1_template(self):
|
||||||
|
# Load v1 and v2 templates, and get their actions
|
||||||
|
v1_action = self._get_template_single_action(self.V1_MISTRAL_TEMPLATE)
|
||||||
|
v2_action = self._get_template_single_action(self.V2_MISTRAL_TEMPLATE)
|
||||||
|
|
||||||
|
# Validate that the action definition is identical (since v1 template
|
||||||
|
# should be converted to v2 format)
|
||||||
|
self._assert_equal_actions(v1_action, v2_action)
|
||||||
|
|
||||||
|
def _get_template_single_action(self, template_file):
|
||||||
|
template_path = '%s/templates/version/%s' % (utils.get_resources_dir(),
|
||||||
|
template_file)
|
||||||
|
template_definition = file_utils.load_yaml_file(template_path, True)
|
||||||
|
template_data = TemplateLoader().load(template_definition)
|
||||||
|
scenarios = template_data.scenarios
|
||||||
|
self.assertIsNotNone(scenarios, 'Template should include a scenario')
|
||||||
|
self.assertEqual(1, len(scenarios),
|
||||||
|
'Template should include a single scenario')
|
||||||
|
actions = scenarios[0].actions
|
||||||
|
self.assertIsNotNone(actions, 'Scenario should include an action')
|
||||||
|
self.assertEqual(1, len(actions),
|
||||||
|
'Scenario should include a single action')
|
||||||
|
return actions[0]
|
||||||
|
|
||||||
|
def _assert_equal_actions(self, action1, action2):
|
||||||
|
"""Compare all action fields except from the id"""
|
||||||
|
self.assertEqual(action1.type, action2.type,
|
||||||
|
'Action types should be equal')
|
||||||
|
self.assert_dict_equal(action1.targets, action2.targets,
|
||||||
|
'Action targets should be equal')
|
||||||
|
self.assert_dict_equal(action1.properties, action2.properties,
|
||||||
|
'Action targets should be equal')
|
||||||
|
|
||||||
def _validate_strict_equal(self,
|
def _validate_strict_equal(self,
|
||||||
template_data,
|
template_data,
|
||||||
expected_entities,
|
expected_entities,
|
||||||
|
@ -14,14 +14,13 @@
|
|||||||
|
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from oslo_log import log as logging
|
from oslo_log import log as logging
|
||||||
from vitrage_tempest_tests.tests.base import BaseVitrageTempest
|
|
||||||
|
from vitrage_tempest_tests.tests.e2e.test_basic_actions import TestActionsBase
|
||||||
|
|
||||||
LOG = logging.getLogger(__name__)
|
LOG = logging.getLogger(__name__)
|
||||||
DOWN = 'down'
|
|
||||||
UP = 'up'
|
|
||||||
|
|
||||||
|
|
||||||
class BaseTestEvents(BaseVitrageTempest):
|
class BaseTestEvents(TestActionsBase):
|
||||||
"""Test class for Vitrage event API"""
|
"""Test class for Vitrage event API"""
|
||||||
|
|
||||||
# noinspection PyPep8Naming
|
# noinspection PyPep8Naming
|
||||||
|
@ -21,7 +21,7 @@ from vitrage.common.constants import EntityCategory
|
|||||||
from vitrage.common.constants import EventProperties as EventProps
|
from vitrage.common.constants import EventProperties as EventProps
|
||||||
from vitrage.common.constants import VertexProperties as VProps
|
from vitrage.common.constants import VertexProperties as VProps
|
||||||
from vitrage_tempest_tests.tests.api.event.base import BaseTestEvents
|
from vitrage_tempest_tests.tests.api.event.base import BaseTestEvents
|
||||||
from vitrage_tempest_tests.tests.api.event.base import DOWN
|
from vitrage_tempest_tests.tests.common.vitrage_utils import DOWN
|
||||||
from vitrage_tempest_tests.tests.utils import wait_for_answer
|
from vitrage_tempest_tests.tests.utils import wait_for_answer
|
||||||
|
|
||||||
|
|
||||||
|
@ -15,11 +15,12 @@ from datetime import datetime
|
|||||||
|
|
||||||
from vitrage.datasources import NOVA_HOST_DATASOURCE
|
from vitrage.datasources import NOVA_HOST_DATASOURCE
|
||||||
from vitrage.datasources import NOVA_INSTANCE_DATASOURCE
|
from vitrage.datasources import NOVA_INSTANCE_DATASOURCE
|
||||||
from vitrage_tempest_tests.tests.api.event.base import DOWN
|
|
||||||
from vitrage_tempest_tests.tests.api.event.base import UP
|
|
||||||
from vitrage_tempest_tests.tests.common import general_utils as g_utils
|
from vitrage_tempest_tests.tests.common import general_utils as g_utils
|
||||||
from vitrage_tempest_tests.tests.common.tempest_clients import TempestClients
|
from vitrage_tempest_tests.tests.common.tempest_clients import TempestClients
|
||||||
|
|
||||||
|
DOWN = 'down'
|
||||||
|
UP = 'up'
|
||||||
|
|
||||||
|
|
||||||
def generate_fake_host_alarm(hostname, event_type, enabled=True):
|
def generate_fake_host_alarm(hostname, event_type, enabled=True):
|
||||||
details = {
|
details = {
|
||||||
|
@ -17,10 +17,7 @@ from testtools.matchers import HasLength
|
|||||||
|
|
||||||
from vitrage import os_clients
|
from vitrage import os_clients
|
||||||
from vitrage_tempest_tests.tests.api.event.base import BaseTestEvents
|
from vitrage_tempest_tests.tests.api.event.base import BaseTestEvents
|
||||||
from vitrage_tempest_tests.tests.api.event.base import DOWN
|
|
||||||
from vitrage_tempest_tests.tests.api.event.base import UP
|
|
||||||
from vitrage_tempest_tests.tests.common.tempest_clients import TempestClients
|
from vitrage_tempest_tests.tests.common.tempest_clients import TempestClients
|
||||||
from vitrage_tempest_tests.tests.common import vitrage_utils
|
|
||||||
from vitrage_tempest_tests.tests import utils
|
from vitrage_tempest_tests.tests import utils
|
||||||
from vitrage_tempest_tests.tests.utils import wait_for_status
|
from vitrage_tempest_tests.tests.utils import wait_for_status
|
||||||
|
|
||||||
@ -46,25 +43,34 @@ wf_for_tempest_test_1234:
|
|||||||
|
|
||||||
class TestMistralNotifier(BaseTestEvents):
|
class TestMistralNotifier(BaseTestEvents):
|
||||||
|
|
||||||
|
TRIGGER_ALARM_1 = "notifiers.mistral.trigger.alarm.1"
|
||||||
|
TRIGGER_ALARM_2 = "notifiers.mistral.trigger.alarm.2"
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def setUpClass(cls):
|
def setUpClass(cls):
|
||||||
super(TestMistralNotifier, cls).setUpClass()
|
super(TestMistralNotifier, cls).setUpClass()
|
||||||
cls.mistral_client = os_clients.mistral_client(cls.conf)
|
cls.mistral_client = os_clients.mistral_client(cls.conf)
|
||||||
|
|
||||||
@utils.tempest_logger
|
@utils.tempest_logger
|
||||||
def test_execute_mistral(self):
|
def test_execute_mistral_v1(self):
|
||||||
hostname = vitrage_utils.get_first_host()['name']
|
self._do_test_execute_mistral(self.TRIGGER_ALARM_1)
|
||||||
|
|
||||||
|
@utils.tempest_logger
|
||||||
|
def test_execute_mistral_v2(self):
|
||||||
|
self._do_test_execute_mistral(self.TRIGGER_ALARM_2)
|
||||||
|
|
||||||
|
def _do_test_execute_mistral(self, trigger_alarm):
|
||||||
workflows = self.mistral_client.workflows.list()
|
workflows = self.mistral_client.workflows.list()
|
||||||
self.assertIsNotNone(workflows)
|
self.assertIsNotNone(workflows, 'Failed to get the list of workflows')
|
||||||
num_workflows = len(workflows)
|
num_workflows = len(workflows)
|
||||||
|
|
||||||
executions = self.mistral_client.executions.list()
|
executions = self.mistral_client.executions.list()
|
||||||
self.assertIsNotNone(executions)
|
self.assertIsNotNone(executions,
|
||||||
|
'Failed to get the list of workflow executions')
|
||||||
num_executions = len(executions)
|
num_executions = len(executions)
|
||||||
|
|
||||||
alarms = utils.wait_for_answer(2, 0.5, self._check_alarms)
|
alarms = utils.wait_for_answer(2, 0.5, self._check_alarms)
|
||||||
self.assertIsNotNone(alarms)
|
self.assertIsNotNone(alarms, 'Failed to get the list of alarms')
|
||||||
num_alarms = len(alarms)
|
num_alarms = len(alarms)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
@ -73,54 +79,58 @@ class TestMistralNotifier(BaseTestEvents):
|
|||||||
|
|
||||||
# Validate the workflow creation
|
# Validate the workflow creation
|
||||||
workflows = self.mistral_client.workflows.list()
|
workflows = self.mistral_client.workflows.list()
|
||||||
self.assertIsNotNone(workflows)
|
self.assertIsNotNone(workflows,
|
||||||
self.assertThat(workflows, HasLength(num_workflows + 1))
|
'Failed to get the list of workflows')
|
||||||
|
self.assertThat(workflows, HasLength(num_workflows + 1),
|
||||||
|
'Mistral workflow was not created')
|
||||||
|
|
||||||
# Send a Doctor event that should generate an alarm. According to
|
# Trigger an alarm. According to v1_execute_mistral.yaml template,
|
||||||
# execute_mistral.yaml template, the alarm should cause execution
|
# the alarm should cause execution of the workflow
|
||||||
# of the workflow
|
self._trigger_do_action(trigger_alarm)
|
||||||
details = self._create_doctor_event_details(hostname, DOWN)
|
|
||||||
self._post_event(details)
|
|
||||||
|
|
||||||
# Wait for the alarm to be raised
|
# Wait for the alarm to be raised
|
||||||
self.assertTrue(wait_for_status(
|
self.assertTrue(wait_for_status(
|
||||||
10,
|
10,
|
||||||
self._check_num_vitrage_alarms,
|
self._check_num_vitrage_alarms,
|
||||||
num_alarms=num_alarms + 1))
|
num_alarms=num_alarms + 1),
|
||||||
|
'Trigger alarm was not raised')
|
||||||
|
|
||||||
# Wait for the Mistral workflow execution
|
# Wait for the Mistral workflow execution
|
||||||
self.assertTrue(wait_for_status(
|
self.assertTrue(wait_for_status(
|
||||||
20,
|
20,
|
||||||
self._check_mistral_workflow_execution,
|
self._check_mistral_workflow_execution,
|
||||||
num_executions=num_executions + 1))
|
num_executions=num_executions + 1),
|
||||||
|
'Mistral workflow was not executed')
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
self._handle_exception(e)
|
self._handle_exception(e)
|
||||||
raise
|
raise
|
||||||
finally:
|
finally:
|
||||||
self._rollback_to_default(WF_NAME, num_workflows,
|
self._rollback_to_default(WF_NAME, num_workflows,
|
||||||
hostname, num_alarms)
|
trigger_alarm, num_alarms)
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def _rollback_to_default(self, workflow_name, num_workflows,
|
def _rollback_to_default(self, workflow_name, num_workflows,
|
||||||
hostname, num_alarms):
|
trigger_alarm, num_alarms):
|
||||||
# Delete the workflow
|
# Delete the workflow
|
||||||
self.mistral_client.workflows.delete(workflow_name)
|
self.mistral_client.workflows.delete(workflow_name)
|
||||||
|
|
||||||
workflows = self.mistral_client.workflows.list()
|
workflows = self.mistral_client.workflows.list()
|
||||||
self.assertIsNotNone(workflows)
|
self.assertIsNotNone(workflows, 'Failed to get the list of workflows')
|
||||||
self.assertThat(workflows, HasLength(num_workflows))
|
self.assertThat(workflows, HasLength(num_workflows),
|
||||||
|
'Failed to remove the test workflow')
|
||||||
|
|
||||||
# Clear the host down event and wait for the alarm to be deleted
|
# Clear the trigger alarm and wait it to be deleted
|
||||||
details = self._create_doctor_event_details(hostname, UP)
|
self._trigger_undo_action(trigger_alarm)
|
||||||
self._post_event(details)
|
|
||||||
|
|
||||||
self.assertTrue(wait_for_status(
|
self.assertTrue(wait_for_status(
|
||||||
10,
|
10,
|
||||||
self._check_num_vitrage_alarms,
|
self._check_num_vitrage_alarms,
|
||||||
num_alarms=num_alarms))
|
num_alarms=num_alarms),
|
||||||
|
'Vitrage trigger alarm was not deleted')
|
||||||
|
|
||||||
def _check_num_vitrage_alarms(self, num_alarms):
|
@staticmethod
|
||||||
|
def _check_num_vitrage_alarms(num_alarms):
|
||||||
vitrage_alarms = TempestClients.vitrage().alarm.list(vitrage_id='all',
|
vitrage_alarms = TempestClients.vitrage().alarm.list(vitrage_id='all',
|
||||||
all_tenants=True)
|
all_tenants=True)
|
||||||
if len(vitrage_alarms) == num_alarms:
|
if len(vitrage_alarms) == num_alarms:
|
||||||
|
@ -0,0 +1,29 @@
|
|||||||
|
metadata:
|
||||||
|
version: 1
|
||||||
|
name: v1_execute_mistral
|
||||||
|
description: execute mistral
|
||||||
|
definitions:
|
||||||
|
entities:
|
||||||
|
- entity:
|
||||||
|
category: ALARM
|
||||||
|
name: notifiers.mistral.trigger.alarm.1
|
||||||
|
template_id: alarm
|
||||||
|
- entity:
|
||||||
|
category: RESOURCE
|
||||||
|
type: nova.host
|
||||||
|
template_id: host
|
||||||
|
relationships:
|
||||||
|
- relationship:
|
||||||
|
source: alarm
|
||||||
|
relationship_type: on
|
||||||
|
target: host
|
||||||
|
template_id : alarm_on_host
|
||||||
|
scenarios:
|
||||||
|
- scenario:
|
||||||
|
condition: alarm_on_host
|
||||||
|
actions:
|
||||||
|
- action:
|
||||||
|
action_type: execute_mistral
|
||||||
|
properties:
|
||||||
|
workflow: wf_for_tempest_test_1234
|
||||||
|
farewell: Hello and Goodbye
|
@ -0,0 +1,30 @@
|
|||||||
|
metadata:
|
||||||
|
version: 2
|
||||||
|
name: v2_execute_mistral
|
||||||
|
description: execute mistral
|
||||||
|
definitions:
|
||||||
|
entities:
|
||||||
|
- entity:
|
||||||
|
category: ALARM
|
||||||
|
name: notifiers.mistral.trigger.alarm.2
|
||||||
|
template_id: alarm
|
||||||
|
- entity:
|
||||||
|
category: RESOURCE
|
||||||
|
type: nova.host
|
||||||
|
template_id: host
|
||||||
|
relationships:
|
||||||
|
- relationship:
|
||||||
|
source: alarm
|
||||||
|
relationship_type: on
|
||||||
|
target: host
|
||||||
|
template_id : alarm_on_host
|
||||||
|
scenarios:
|
||||||
|
- scenario:
|
||||||
|
condition: alarm_on_host
|
||||||
|
actions:
|
||||||
|
- action:
|
||||||
|
action_type: execute_mistral
|
||||||
|
properties:
|
||||||
|
workflow: wf_for_tempest_test_1234
|
||||||
|
input:
|
||||||
|
farewell: Hello and Goodbye
|
Loading…
Reference in New Issue
Block a user