Merge "add nova force-down-notifier"

This commit is contained in:
Jenkins 2016-07-13 14:03:20 +00:00 committed by Gerrit Code Review
commit 34cf3cfb1d
9 changed files with 229 additions and 44 deletions

View File

@ -26,7 +26,7 @@ LOG = log.getLogger(__name__)
OPTS = [
cfg.StrOpt('aodh_version', default='2', help='Aodh version'),
cfg.FloatOpt('nova_version', default='2.0', help='Nova version'),
cfg.FloatOpt('nova_version', default='2.11', help='Nova version'),
cfg.StrOpt('cinder_version', default='1', help='Cinder version'),
]

View File

@ -24,6 +24,6 @@ OPTS = [
),
cfg.StrOpt('notifier_topic',
default='vitrage.graph',
help='The topic that vitrage-graph uses for alarm '
help='The topic that vitrage-graph uses for graph '
'notification messages.'),
]

View File

@ -24,7 +24,7 @@ from vitrage.messaging import get_transport
LOG = log.getLogger(__name__)
class DeducedAlarmNotifier(object):
class GraphNotifier(object):
"""Allows writing to message bus"""
def __init__(self, conf):
self.oslo_notifier = None
@ -32,16 +32,16 @@ class DeducedAlarmNotifier(object):
topic = conf.entity_graph.notifier_topic
notifier_plugins = conf.notifiers
if not topic or not notifier_plugins:
LOG.info('DeducedAlarmNotifier is disabled')
LOG.info('Graph Notifier is disabled')
return
self.oslo_notifier = oslo_messaging.Notifier(
get_transport(conf),
driver='messagingv2',
publisher_id='vitrage.deduced',
publisher_id='vitrage.graph',
topic=topic)
except Exception as e:
LOG.info('DeducedAlarmNotifier missing configuration %s' % str(e))
LOG.info('Graph Notifier - missing configuration %s' % str(e))
@property
def enabled(self):
@ -57,38 +57,67 @@ class DeducedAlarmNotifier(object):
change that happened. Deleted elements should arrive with the
is_deleted property set to True
"""
notification_type = _get_notification_type(before, current, is_vertex)
if not notification_type:
notification_types = _get_notification_type(before, current, is_vertex)
if not notification_types:
return
LOG.debug('DeducedAlarmNotifier : %s', notification_type)
LOG.debug('DeducedAlarmNotifier : %s', current.properties)
LOG.info('notification_types : %s', str(notification_types))
LOG.info('notification properties : %s', current.properties)
try:
self.oslo_notifier.info({}, notification_type, current.properties)
except Exception as e:
LOG.exception('DeducedAlarmNotifier cannot notify - %s', e)
for notification_type in notification_types:
try:
self.oslo_notifier.info(
{},
notification_type,
current.properties)
except Exception as e:
LOG.exception('Cannot notify - %s - %s', notification_type, e)
def _get_notification_type(before, current, is_vertex):
if not is_vertex:
return None
if not _is_active_deduced_alarm(before) and \
_is_active_deduced_alarm(current):
return NotifierEventTypes.ACTIVATE_DEDUCED_ALARM_EVENT
if _is_active_deduced_alarm(before) and \
not _is_active_deduced_alarm(current):
return NotifierEventTypes.DEACTIVATE_DEDUCED_ALARM_EVENT
def notification_type(is_active,
activate_event_type,
deactivate_event_type):
if not is_active(before):
if is_active(current):
return activate_event_type
else:
if not is_active(current):
return deactivate_event_type
notification_types = [
notification_type(_is_active_deduced_alarm,
NotifierEventTypes.ACTIVATE_DEDUCED_ALARM_EVENT,
NotifierEventTypes.DEACTIVATE_DEDUCED_ALARM_EVENT),
notification_type(_is_marked_down,
NotifierEventTypes.ACTIVATE_MARK_DOWN_EVENT,
NotifierEventTypes.DEACTIVATE_MARK_DOWN_EVENT),
]
return list(filter(None, notification_types))
def _is_active_deduced_alarm(vertex):
if not vertex:
return False
if vertex.get(VProps.CATEGORY) == EntityCategory.ALARM and \
vertex.get(VProps.TYPE) == evaluator.VITRAGE_TYPE:
return _is_relevant_vertex(vertex)
return False
if not (vertex.get(VProps.CATEGORY) == EntityCategory.ALARM and
vertex.get(VProps.TYPE) == evaluator.VITRAGE_TYPE):
def _is_marked_down(vertex):
if not vertex:
return False
if vertex.get(VProps.CATEGORY) == EntityCategory.RESOURCE and \
vertex.get(VProps.IS_MARKED_DOWN) is True:
return _is_relevant_vertex(vertex)
return False
def _is_relevant_vertex(vertex):
if vertex.get(VProps.IS_DELETED, False) or \
vertex.get(VProps.IS_PLACEHOLDER, False):
return False

View File

@ -22,7 +22,7 @@ from vitrage.entity_graph.mappings.datasource_info_mapper import \
DatasourceInfoMapper
from vitrage.entity_graph.processor import base as processor
from vitrage.entity_graph.processor import entity_graph
from vitrage.entity_graph.processor.notifier import DeducedAlarmNotifier
from vitrage.entity_graph.processor.notifier import GraphNotifier
from vitrage.entity_graph.processor import processor_utils as PUtils
from vitrage.entity_graph.transformer_manager import TransformerManager
from vitrage.graph import Direction
@ -41,7 +41,7 @@ class Processor(processor.ProcessorBase):
self.initialization_status = initialization_status
self.entity_graph = entity_graph.EntityGraph("Entity Graph") if \
e_graph is None else e_graph
self._notifier = DeducedAlarmNotifier(conf)
self._notifier = GraphNotifier(conf)
def process_event(self, event):
"""Decides which action to run on given event

View File

@ -16,5 +16,5 @@ from oslo_config import cfg
OPTS = [
cfg.ListOpt('notifiers',
help='Names of enabled notifiers (example aodh)'),
help='Names of enabled notifiers (example aodh, nova)'),
]

View File

@ -0,0 +1,15 @@
# 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.
pass

View File

@ -0,0 +1,51 @@
# 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 import clients
from vitrage.common.constants import NotifierEventTypes
from vitrage.common.constants import VertexProperties as VProps
from vitrage.datasources import NOVA_HOST_DATASOURCE
from vitrage.notifier.plugins.base import NotifierBase
LOG = logging.getLogger(__name__)
class NovaNotifier(NotifierBase):
@staticmethod
def get_notifier_name():
return 'nova'
def __init__(self, conf):
super(NovaNotifier, self).__init__(conf)
self.client = clients.nova_client(conf)
def process_event(self, data, event_type):
if data and data.get(VProps.TYPE) is NOVA_HOST_DATASOURCE:
if event_type == NotifierEventTypes.ACTIVATE_MARK_DOWN_EVENT:
self._mark_host_down(data.get(VProps.ID), True)
elif event_type == NotifierEventTypes.DEACTIVATE_MARK_DOWN_EVENT:
self._mark_host_down(data.get(VProps.ID), False)
def _mark_host_down(self, host_id, is_down):
try:
LOG.info('Nova services.force_down - host id: %s, is_down: %s',
str(host_id), str(is_down))
response = self.client.services.force_down(
host_id, 'nova-compute', is_down)
LOG.info('RESPONSE %s', str(response))
except Exception as e:
LOG.exception('Failed to services.force_down - %s', e)
return

View File

@ -18,6 +18,7 @@ from oslo_service import service as os_service
from vitrage import messaging
from vitrage.notifier.plugins.aodh.aodh_notifier import AodhNotifier
from vitrage.notifier.plugins.nova.nova_notifier import NovaNotifier
LOG = log.getLogger(__name__)
@ -59,7 +60,7 @@ class VitrageNotifierService(os_service.Service):
if not conf_notifier_names:
LOG.info('There are no notifier plugins in configuration')
return []
for plugin in [AodhNotifier]:
for plugin in [AodhNotifier, NovaNotifier]:
plugin_name = plugin.get_notifier_name()
if plugin_name in conf_notifier_names:
LOG.info('Notifier plugin %s started', plugin_name)

View File

@ -18,6 +18,7 @@ test_vitrage graph
Tests for `vitrage` graph driver
"""
import copy
from vitrage.common.constants import EntityCategory
from vitrage.common.constants import NotifierEventTypes as NType
@ -33,16 +34,14 @@ resource = Vertex('123', {
VProps.TYPE: 'some_resource_type',
VProps.IS_DELETED: False,
VProps.IS_PLACEHOLDER: False,
}
)
})
deduced_alarm = Vertex('123', {
VProps.CATEGORY: EntityCategory.ALARM,
VProps.TYPE: evaluator.VITRAGE_TYPE,
VProps.IS_DELETED: False,
VProps.IS_PLACEHOLDER: False,
}
)
})
non_deduced_alarm = Vertex('123', {
@ -50,56 +49,146 @@ non_deduced_alarm = Vertex('123', {
VProps.TYPE: 'TEST_ALARM',
VProps.IS_DELETED: False,
VProps.IS_PLACEHOLDER: True,
}
)
})
deleted_alarm = Vertex('123', {
VProps.CATEGORY: EntityCategory.ALARM,
VProps.TYPE: evaluator.VITRAGE_TYPE,
VProps.IS_DELETED: True,
VProps.IS_PLACEHOLDER: False,
}
)
})
placeholder_alarm = Vertex('123', {
VProps.CATEGORY: EntityCategory.ALARM,
VProps.TYPE: evaluator.VITRAGE_TYPE,
VProps.IS_DELETED: False,
VProps.IS_PLACEHOLDER: True,
}
)
})
host = Vertex('123', {
VProps.CATEGORY: EntityCategory.RESOURCE,
VProps.TYPE: 'nova.host',
VProps.IS_DELETED: False,
VProps.IS_PLACEHOLDER: False,
})
forced_down_host = Vertex('123', {
VProps.CATEGORY: EntityCategory.RESOURCE,
VProps.TYPE: 'nova.host',
VProps.IS_DELETED: False,
VProps.IS_PLACEHOLDER: False,
VProps.IS_MARKED_DOWN: True,
})
class GraphTest(base.BaseTest):
def get_first(self, lst):
self.assertIsNotNone(lst)
return lst[0] if len(lst) > 0 else None
def test_notification_type_new_alarm(self):
ret = _get_notification_type(None, deduced_alarm, True)
self.assertEqual(NType.ACTIVATE_DEDUCED_ALARM_EVENT, ret,
self.assertEqual(NType.ACTIVATE_DEDUCED_ALARM_EVENT,
self.get_first(ret),
'new alarm should notify activate')
ret = _get_notification_type(None, non_deduced_alarm, True)
self.assertIsNone(ret, 'alarm that is not a deduced alarm')
self.assertIsNone(self.get_first(ret),
'alarm that is not a deduced alarm')
def test_notification_type_deleted_alarm(self):
ret = _get_notification_type(deduced_alarm, deleted_alarm, True)
self.assertEqual(NType.DEACTIVATE_DEDUCED_ALARM_EVENT, ret,
self.assertEqual(NType.DEACTIVATE_DEDUCED_ALARM_EVENT,
self.get_first(ret),
'deleted alarm should notify deactivate')
def test_notification_type_resource_vertex(self):
ret = _get_notification_type(None, resource, True)
self.assertIsNone(ret, 'any non alarm vertex should be ignored')
self.assertIsNone(self.get_first(ret),
'any non alarm vertex should be ignored')
def test_notification_type_updated_alarm(self):
ret = _get_notification_type(deduced_alarm, deduced_alarm, True)
self.assertIsNone(ret, 'A not new alarm vertex should be ignored')
self.assertIsNone(self.get_first(ret),
'A not new alarm vertex should be ignored')
ret = _get_notification_type(deleted_alarm, deduced_alarm, True)
self.assertEqual(NType.ACTIVATE_DEDUCED_ALARM_EVENT, ret,
self.assertEqual(NType.ACTIVATE_DEDUCED_ALARM_EVENT,
self.get_first(ret),
'old alarm become not deleted should notify activate')
ret = _get_notification_type(placeholder_alarm, deduced_alarm, True)
self.assertEqual(NType.ACTIVATE_DEDUCED_ALARM_EVENT, ret,
self.assertEqual(NType.ACTIVATE_DEDUCED_ALARM_EVENT,
self.get_first(ret),
'placeholder become active should notify activate')
def test_notification_type_placeholder_alarm(self):
ret = _get_notification_type(None, placeholder_alarm, True)
self.assertIsNone(ret, 'A not new alarm vertex should be ignored')
self.assertIsNone(self.get_first(ret),
'A not new alarm vertex should be ignored')
def test_notification_type_new_host(self):
ret = _get_notification_type(None, forced_down_host, True)
self.assertEqual(NType.ACTIVATE_MARK_DOWN_EVENT,
self.get_first(ret),
'new host with forced_down should notify activate')
ret = _get_notification_type(None, host, True)
self.assertIsNone(self.get_first(ret), 'host without forced_down')
def test_notification_type_deleted_host(self):
deleted_host = copy.deepcopy(forced_down_host)
deleted_host[VProps.IS_DELETED] = True
ret = _get_notification_type(forced_down_host, deleted_host, True)
self.assertEqual(
NType.DEACTIVATE_MARK_DOWN_EVENT,
self.get_first(ret),
'deleted host with forced_down should notify deactivate')
deleted_host = copy.deepcopy(host)
deleted_host[VProps.IS_DELETED] = True
ret = _get_notification_type(forced_down_host, deleted_host, True)
self.assertEqual(
NType.DEACTIVATE_MARK_DOWN_EVENT,
self.get_first(ret),
'deleted host with forced_down should notify deactivate')
deleted_host = copy.deepcopy(host)
deleted_host[VProps.IS_DELETED] = True
ret = _get_notification_type(host, deleted_host, True)
self.assertIsNone(
self.get_first(ret),
'deleted host without forced_down should not notify')
def test_notification_type_updated_host(self):
ret = _get_notification_type(forced_down_host, forced_down_host, True)
self.assertIsNone(self.get_first(ret),
'A not new host should be ignored')
deleted_host = copy.deepcopy(forced_down_host)
deleted_host[VProps.IS_DELETED] = True
ret = _get_notification_type(deleted_host, forced_down_host, True)
self.assertEqual(NType.ACTIVATE_MARK_DOWN_EVENT,
self.get_first(ret),
'old host become not deleted should notify activate')
deleted_host = copy.deepcopy(forced_down_host)
deleted_host[VProps.IS_DELETED] = True
ret = _get_notification_type(deleted_host, host, True)
self.assertIsNone(self.get_first(ret),
'old host become not deleted should not notify')
placeholder_host = copy.deepcopy(forced_down_host)
placeholder_host[VProps.IS_PLACEHOLDER] = True
ret = _get_notification_type(placeholder_host, forced_down_host, True)
self.assertEqual(NType.ACTIVATE_MARK_DOWN_EVENT,
self.get_first(ret),
'placeholder become active should notify activate')
def test_notification_type_placeholder_host(self):
placeholder_host = copy.deepcopy(forced_down_host)
placeholder_host[VProps.IS_PLACEHOLDER] = True
ret = _get_notification_type(None, placeholder_host, True)
self.assertIsNone(self.get_first(ret),
'A not new host vertex should be ignored')