Merge "Add mark-down action"

This commit is contained in:
Jenkins 2016-07-12 14:29:44 +00:00 committed by Gerrit Code Review
commit eab092aa8b
13 changed files with 286 additions and 14 deletions

View File

@ -312,9 +312,62 @@ Common parameters and their acceptable values - for writing templates
| action | action_type | raise_alarm, | |
| | | set_state, | |
| | | add_causal_relationship | |
| | | mark_down | |
+-------------------+---------------+-------------------------+------------------------------------+
Supported Actions
-----------------
raise_alarm
^^^^^^^^^^^
Raise a deduced alarm on a target entity
::
action:
action_type : raise_alarm
properties:
alarm_name: some problem # mandatory; string that is valid variable name
severity: critical # mandatory; should match values in "vitrage.yaml"
action_target:
target: instance # mandatory. entity (from the definitions section) to raise an alarm on. Should not be an alarm.
set_state
^^^^^^^^^^^
Set state of specified entity. This will directly affect the state as seen in vitrage, but will not impact the state at the relevant datasource of this entity.
::
action:
action_type : set_state
properties:
state: error # mandatory; should match values in the relevant datasource_values YAML file for this entity.
action_target:
target: host # mandatory. entity (from the definitions section) to change state
add_causal_relationship
^^^^^^^^^^^
Add a causal relationship between alarms.
::
action:
action_type : add_causal_relationship
action_target:
source: host_alarm # mandatory. the alarm that caused the target alarm (name from the definitions section)
target: instance_alarm # mandatory. the alarm that was caused by the source alarm (name from the definitions section)
mark_down
^^^^^^^^^
Set an entity marked_down field.
This can be used along with nova notifier to call force_down for a host
::
action:
action_type : mark_down
action_target:
target: instance # mandatory. entity (from the definitions section) to be marked as down
Future support & Open Issues
============================

View File

@ -32,6 +32,7 @@ class VertexProperties(object):
AGGREGATED_SEVERITY = 'aggregated_severity'
OPERATIONAL_SEVERITY = 'operational_severity'
VITRAGE_ID = 'vitrage_id'
IS_MARKED_DOWN = 'is_marked_down'
INFO = 'info'
GRAPH_INDEX = 'graph_index'
@ -93,3 +94,5 @@ class EventAction(object):
class NotifierEventTypes(object):
ACTIVATE_DEDUCED_ALARM_EVENT = 'vitrage.deduced_alarm.activate'
DEACTIVATE_DEDUCED_ALARM_EVENT = 'vitrage.deduced_alarm.deactivate'
ACTIVATE_MARK_DOWN_EVENT = 'vitrage.mark_down.activate'
DEACTIVATE_MARK_DOWN_EVENT = 'vitrage.mark_down.deactivate'

View File

@ -32,6 +32,7 @@ from vitrage.evaluator.actions.recipes.action_steps import UPDATE_VERTEX
from vitrage.evaluator.actions.recipes.add_causal_relationship import \
AddCausalRelationship
from vitrage.evaluator.actions.recipes.base import EVALUATOR_EVENT_TYPE
from vitrage.evaluator.actions.recipes.mark_down import MarkDown
from vitrage.evaluator.actions.recipes.raise_alarm import RaiseAlarm
from vitrage.evaluator.actions.recipes.set_state import SetState
@ -128,4 +129,7 @@ class ActionExecutor(object):
"%s.%s" % (AddCausalRelationship.__module__,
AddCausalRelationship.__name__))
recipes[ActionType.MARK_DOWN] = importutils.import_object(
"%s.%s" % (MarkDown.__module__, MarkDown.__name__))
return recipes

View File

@ -18,10 +18,12 @@ class ActionType(object):
SET_STATE = 'set_state'
RAISE_ALARM = 'raise_alarm'
ADD_CAUSAL_RELATIONSHIP = 'add_causal_relationship'
MARK_DOWN = 'mark_down'
action_types = [ActionType.SET_STATE,
ActionType.RAISE_ALARM,
ActionType.ADD_CAUSAL_RELATIONSHIP]
ActionType.ADD_CAUSAL_RELATIONSHIP,
ActionType.MARK_DOWN]
class ActionMode(object):

View File

@ -62,7 +62,8 @@ class EvaluatorEventTransformer(transformer_base.TransformerBase):
if event_type == UPDATE_VERTEX:
properties = {
VProps.VITRAGE_STATE: event[VProps.VITRAGE_STATE],
VProps.VITRAGE_STATE: event.get(VProps.VITRAGE_STATE),
VProps.IS_MARKED_DOWN: event.get(VProps.IS_MARKED_DOWN),
VProps.UPDATE_TIMESTAMP: update_timestamp,
VProps.SAMPLE_TIMESTAMP: event[VProps.SAMPLE_TIMESTAMP],
VProps.IS_PLACEHOLDER: False

View File

@ -16,7 +16,7 @@ from vitrage.common.constants import VertexProperties as VProps
from vitrage.evaluator.template_fields import TemplateFields
class CausalTools(object):
class BaselineTools(object):
@staticmethod
def get_score(action_info):
return 1 # no priorities

View File

@ -0,0 +1,61 @@
# 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.common.constants import VertexProperties as VProps
from vitrage.evaluator.actions.recipes.action_steps import UPDATE_VERTEX
from vitrage.evaluator.actions.recipes import base
from vitrage.evaluator.actions.recipes.base import ActionStepWrapper
from vitrage.evaluator.template_fields import TemplateFields as TFields
class MarkDown(base.Recipe):
"""Mark A host as down.
The 'get_do_recipe' and 'get_undo_recipe' receive action_spec as input.
The action_spec contains the following fields: type, targets and
properties. example input:
action_spec = ActionSpecs('type'= {'set_state'},
'targets'= {target: Vertex},
'properties' = {is_marked_down : True}
"""
@staticmethod
def get_do_recipe(action_spec):
update_vertex_step = MarkDown._get_update_vertex_step(
action_spec.targets[TFields.TARGET].vertex_id,
True)
return [update_vertex_step]
@staticmethod
def get_undo_recipe(action_spec):
update_vertex_step = MarkDown._get_update_vertex_step(
action_spec.targets[TFields.TARGET].vertex_id,
False)
return [update_vertex_step]
@staticmethod
def _get_update_vertex_step(target_id, is_marked_down):
update_vertex_params = {
VProps.VITRAGE_ID: target_id,
VProps.IS_MARKED_DOWN: is_marked_down
}
update_vertex_step = ActionStepWrapper(UPDATE_VERTEX,
update_vertex_params)
return update_vertex_step

View File

@ -281,7 +281,8 @@ class ActionTracker(object):
self._action_tools = {
ActionType.SET_STATE: pt.SetStateTools(all_scores),
ActionType.RAISE_ALARM: pt.RaiseAlarmTools(alarms_score),
ActionType.ADD_CAUSAL_RELATIONSHIP: pt.CausalTools
ActionType.ADD_CAUSAL_RELATIONSHIP: pt.BaselineTools,
ActionType.MARK_DOWN: pt.BaselineTools
}
def get_key(self, action_specs):

View File

@ -68,7 +68,10 @@ status_msgs = {
126: 'raise_alarm action must contain severity field in properties block.',
127: 'raise_alarm action must contain target field in target_action block',
128: 'set_state action must contain state field in properties block.',
129: 'set_state action must contain target field in target_action block',
129: 'set_state action must contain target field in target_action block.',
130: 'add_causal_relationship action must contain target and source field '
'in target_action block',
'in target_action block.',
131: 'mark_down action must contain \'target\' field in'
' \'target_action\' block.',
}

View File

@ -168,17 +168,20 @@ def validate_scenario_actions(actions, entities_ids):
def validate_scenario_action(action, entities_ids):
action_type = action[TemplateFields.ACTION_TYPE]
actions = {
ActionType.RAISE_ALARM: validate_raise_alarm_action,
ActionType.SET_STATE: validate_set_state_action,
ActionType.ADD_CAUSAL_RELATIONSHIP:
validate_add_causal_relationship_action,
ActionType.MARK_DOWN: validate_mark_down_action,
}
if action_type == ActionType.RAISE_ALARM:
return validate_raise_alarm_action(action, entities_ids)
elif action_type == ActionType.SET_STATE:
return validate_set_state_action(action, entities_ids)
elif action_type == ActionType.ADD_CAUSAL_RELATIONSHIP:
return validate_add_causal_relationship_action(action, entities_ids)
else:
if action_type not in actions.keys():
LOG.error('%s status code: %s' % (status_msgs[120], 120))
return get_fault_result(RESULT_DESCRIPTION, 120)
return actions[action_type](action, entities_ids)
def validate_raise_alarm_action(action, entities_ids):
@ -240,6 +243,17 @@ def validate_add_causal_relationship_action(action, entities_ids):
return _validate_template_id(entities_ids, source)
def validate_mark_down_action(action, entities_ids):
action_target = action[TemplateFields.ACTION_TARGET]
if TemplateFields.TARGET not in action_target:
LOG.error('%s status code: %s' % (status_msgs[131], 131))
return get_fault_result(RESULT_DESCRIPTION, 131)
target = action_target[TemplateFields.TARGET]
return _validate_template_id(entities_ids, target)
def _validate_template_id(ids, id_to_check):
if id_to_check not in ids:

View File

@ -246,7 +246,7 @@ def _validate_dict_schema(schema, value):
msg = status_msgs[4] % e
LOG.error('%s status code: %s' % (msg, status_code))
return get_fault_result(RESULT_DESCRIPTION, status_code)
return get_fault_result(RESULT_DESCRIPTION, status_code, msg)
return get_correct_result(RESULT_DESCRIPTION)

View File

@ -0,0 +1,78 @@
# 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 oslo_log import log as logging
from vitrage.common.constants import VertexProperties as VProps
from vitrage.evaluator.actions.base import ActionType
from vitrage.evaluator.actions.recipes.action_steps import UPDATE_VERTEX
from vitrage.evaluator.actions.recipes.mark_down import MarkDown
from vitrage.evaluator.template_data import ActionSpecs
from vitrage.evaluator.template_fields import TemplateFields as TFields
from vitrage.graph import Vertex
from vitrage.tests import base
LOG = logging.getLogger(__name__)
class MarkDownRecipeTest(base.BaseTest):
# noinspection PyPep8Naming
@classmethod
def setUpClass(cls):
cls.target_vertex = Vertex('RESOURCE:nova.host:test1')
targets = {TFields.TARGET: cls.target_vertex}
cls.action_spec = ActionSpecs(ActionType.MARK_DOWN, targets, None)
def test_get_do_recipe(self):
# Test Action
action_steps = MarkDown.get_do_recipe(self.action_spec)
# Test Assertions
# expecting for one step: [update_vertex]
self.assertEqual(1, len(action_steps))
self.assertEqual(UPDATE_VERTEX, action_steps[0].type)
update_vertex_step_params = action_steps[0].params
self.assertEqual(2, len(update_vertex_step_params))
is_marked_down = update_vertex_step_params[VProps.IS_MARKED_DOWN]
self.assertTrue(is_marked_down)
vitrage_id = update_vertex_step_params[VProps.VITRAGE_ID]
self.assertEqual(self.target_vertex.vertex_id, vitrage_id)
def test_get_undo_recipe(self):
# Test Action
action_steps = MarkDown.get_undo_recipe(self.action_spec)
# Test Assertions
# expecting for one step: [update_vertex]
self.assertEqual(1, len(action_steps))
self.assertEqual(UPDATE_VERTEX, action_steps[0].type)
update_vertex_step_params = action_steps[0].params
self.assertEqual(2, len(update_vertex_step_params))
is_marked_down = update_vertex_step_params[VProps.IS_MARKED_DOWN]
self.assertFalse(is_marked_down)
vitrage_id = update_vertex_step_params[VProps.VITRAGE_ID]
self.assertEqual(self.target_vertex.vertex_id, vitrage_id)

View File

@ -228,6 +228,43 @@ class TemplateContentValidatorTest(base.BaseTest):
# Test assertions
self._test_assert_with_fault_result(result, 128)
def test_validate_mark_down_action(self):
# Test setup
ids = ['123', '456', '789']
action = self._create_mark_down_action('123')
# Test action and assertions
result = validator.validate_mark_down_action(action, ids)
# Test Assertions
self._test_assert_with_correct_result(result)
def test_validate_mark_down_action_with_invalid_target_id(self):
# Test setup
ids = ['123', '456', '789']
action = self._create_mark_down_action('unknown')
# Test action
result = validator.validate_mark_down_action(action, ids)
# Test assertions
self._test_assert_with_fault_result(result, 3)
def test_validate_mark_down_action_without_target_id(self):
# Test setup
ids = ['123', '456', '789']
action = self._create_mark_down_action('123')
action[TemplateFields.ACTION_TARGET].pop(TemplateFields.TARGET)
# Test action
result = validator.validate_mark_down_action(action, ids)
# Test assertions
self._test_assert_with_fault_result(result, 131)
def test_validate_add_causal_relationship_action(self):
# Test setup
@ -323,6 +360,9 @@ class TemplateContentValidatorTest(base.BaseTest):
set_state_action = self._create_set_state_action(target)
actions.append({TemplateFields.ACTION: set_state_action})
mark_host_down_action = self._create_mark_down_action(target)
actions.append({TemplateFields.ACTION: mark_host_down_action})
causal_action = self._create_add_causal_relationship_action(target,
source)
actions.append({TemplateFields.ACTION: causal_action})
@ -343,6 +383,18 @@ class TemplateContentValidatorTest(base.BaseTest):
return action
@staticmethod
def _create_mark_down_action(target):
action_target = {
TemplateFields.TARGET: target
}
action = {
TemplateFields.ACTION_TYPE: ActionType.MARK_DOWN,
TemplateFields.ACTION_TARGET: action_target,
}
return action
@staticmethod
def _create_set_state_action(target):