Implement aodh alarm notification
Implement: blueprint aodh-message-bus-notifications Signed-off-by: dongwenjuan <dong.wenjuan@zte.com.cn> Change-Id: Ibd55345dea3b465006cdbd89bff2e67c3ac9daba
This commit is contained in:
parent
c4542eaaa5
commit
d57af3234d
@ -53,4 +53,13 @@ Enabling Vitrage in DevStack
|
||||
notification_topics = notifications,vitrage_notifications
|
||||
notification_driver=messagingv2
|
||||
|
||||
7. Run ``./stack.sh``
|
||||
7. Add this to add notification from aodh to vitrage
|
||||
|
||||
.. code:: bash
|
||||
|
||||
[[post-config|$AODH_CONF]]
|
||||
[oslo_messaging_notifications]
|
||||
driver = messagingv2
|
||||
topics = notifications,vitrage_notifications
|
||||
|
||||
8. Run ``./stack.sh``
|
||||
|
@ -41,6 +41,7 @@ ENABLED_SERVICES+=,vitrage-api,vitrage-graph
|
||||
ENABLED_SERVICES+=,key,aodi-api,aodh-notifier,aodh-evaluator
|
||||
ENABLED_SERVICES+=,ceilometer-alarm-evaluator,ceilometer-alarm-notifier
|
||||
ENABLED_SERVICES+=,ceilometer-api
|
||||
ENABLED_SERVICES+=,aodh-api
|
||||
export ENABLED_SERVICES
|
||||
|
||||
|
||||
@ -69,6 +70,11 @@ notification_topics = notifications,vitrage_notifications
|
||||
notification_driver = messagingv2
|
||||
policy_file = /etc/heat/policy.json-tempest
|
||||
|
||||
[[post-config|\$AODH_CONF]]
|
||||
[oslo_messaging_notifications]
|
||||
driver = messagingv2
|
||||
topics = notifications, vitrage_notifications
|
||||
|
||||
[[post-config|\$VITRAGE_CONF]]
|
||||
[static_physical]
|
||||
changes_interval = 5
|
||||
|
@ -85,15 +85,16 @@ class AlarmDriverBase(DriverBase):
|
||||
def _get_all_alarms(self):
|
||||
alarms = self._get_alarms()
|
||||
self._enrich_alarms(alarms)
|
||||
return self._filter_and_cache_alarms(alarms,
|
||||
AlarmDriverBase._filter_get_all)
|
||||
return self._filter_and_cache_alarms(
|
||||
alarms,
|
||||
self._filter_get_erroneous)
|
||||
|
||||
def _get_changed_alarms(self):
|
||||
alarms = self._get_alarms()
|
||||
self._enrich_alarms(alarms)
|
||||
return self._filter_and_cache_alarms(
|
||||
alarms,
|
||||
AlarmDriverBase._filter_get_changes)
|
||||
self._filter_get_change)
|
||||
|
||||
def _filter_and_cache_alarms(self, alarms, filter_):
|
||||
alarms_to_update = []
|
||||
@ -101,16 +102,11 @@ class AlarmDriverBase(DriverBase):
|
||||
|
||||
for alarm in alarms:
|
||||
alarm_key = self._alarm_key(alarm)
|
||||
old_alarm, timestamp = self.cache.get(alarm_key, (None, None))
|
||||
|
||||
if filter_(self, alarm, old_alarm):
|
||||
# delete state changed alarm: alarm->OK
|
||||
if not self._is_erroneous(alarm):
|
||||
alarm[DSProps.EVENT_TYPE] = GraphAction.DELETE_ENTITY
|
||||
old_alarm = self.cache.get(alarm_key, (None, None))[0]
|
||||
if self._filter_and_cache_alarm(
|
||||
alarm, old_alarm, filter_, now):
|
||||
alarms_to_update.append(alarm)
|
||||
|
||||
self.cache[alarm_key] = alarm, now
|
||||
|
||||
# add alarms that were deleted
|
||||
values = list(self.cache.values())
|
||||
for cached_alarm, timestamp in values:
|
||||
@ -122,13 +118,16 @@ class AlarmDriverBase(DriverBase):
|
||||
|
||||
return alarms_to_update
|
||||
|
||||
def _filter_get_all(self, alarm, old_alarm):
|
||||
def _filter_get_valid(self, alarm, old_alarm):
|
||||
return alarm if self._is_valid(alarm) else None
|
||||
|
||||
def _filter_get_erroneous(self, alarm, old_alarm):
|
||||
return alarm \
|
||||
if self._is_valid(alarm) and \
|
||||
(self._is_erroneous(alarm) or self._is_erroneous(old_alarm)) \
|
||||
else None
|
||||
|
||||
def _filter_get_changes(self, alarm, old_alarm):
|
||||
def _filter_get_change(self, alarm, old_alarm):
|
||||
if not self._is_valid(alarm):
|
||||
return None
|
||||
if self._status_changed(alarm, old_alarm):
|
||||
@ -137,3 +136,8 @@ class AlarmDriverBase(DriverBase):
|
||||
return alarm
|
||||
else:
|
||||
return None
|
||||
|
||||
def _filter_and_cache_alarm(self, alarm, old_alarm, filter_, time):
|
||||
ret = alarm if filter_(alarm, old_alarm) else None
|
||||
self.cache[self._alarm_key(alarm)] = alarm, time
|
||||
return ret
|
||||
|
@ -14,9 +14,11 @@
|
||||
|
||||
from oslo_log import log as logging
|
||||
|
||||
from vitrage.common.constants import DatasourceAction
|
||||
from vitrage.common.constants import DatasourceProperties as DSProps
|
||||
from vitrage.common.constants import EntityCategory
|
||||
from vitrage.common.constants import GraphAction
|
||||
from vitrage.common.exception import VitrageTransformerError
|
||||
from vitrage.datasources.alarm_properties import AlarmProperties as AlarmProps
|
||||
from vitrage.datasources import transformer_base as tbase
|
||||
|
||||
@ -31,6 +33,27 @@ class AlarmTransformerBase(tbase.TransformerBase):
|
||||
def _ok_status(self, entity_event):
|
||||
pass
|
||||
|
||||
def _extract_graph_action(self, entity_event):
|
||||
|
||||
if DSProps.EVENT_TYPE in entity_event and \
|
||||
entity_event[DSProps.EVENT_TYPE] == GraphAction.DELETE_ENTITY:
|
||||
return entity_event[DSProps.EVENT_TYPE]
|
||||
|
||||
datasource_action = entity_event[DSProps.DATASOURCE_ACTION]
|
||||
|
||||
if datasource_action in \
|
||||
(DatasourceAction.UPDATE, DatasourceAction.SNAPSHOT):
|
||||
return GraphAction.DELETE_ENTITY if self._ok_status(entity_event) else \
|
||||
self.GRAPH_ACTION_MAPPING.get(
|
||||
entity_event.get(DSProps.EVENT_TYPE, None),
|
||||
GraphAction.UPDATE_ENTITY)
|
||||
|
||||
if DatasourceAction.INIT_SNAPSHOT == datasource_action:
|
||||
return GraphAction.CREATE_ENTITY
|
||||
|
||||
raise VitrageTransformerError('Invalid datasource action: (%s)'
|
||||
% datasource_action)
|
||||
|
||||
def create_placeholder_vertex(self, **kwargs):
|
||||
LOG.info('An alarm cannot be a placeholder')
|
||||
pass
|
||||
|
@ -28,7 +28,7 @@ OPTS = [
|
||||
help='Aodh driver class path',
|
||||
required=True),
|
||||
cfg.StrOpt('update_method',
|
||||
default=UpdateMethod.PULL,
|
||||
default=UpdateMethod.PUSH,
|
||||
help='None: updates only via Vitrage periodic snapshots.'
|
||||
'Pull: updates every [changes_interval] seconds.'
|
||||
'Push: updates by getting notifications from the'
|
||||
|
@ -14,20 +14,27 @@
|
||||
|
||||
from oslo_log import log
|
||||
|
||||
from vitrage.common.constants import DatasourceAction
|
||||
from vitrage.common.constants import DatasourceProperties as DSProps
|
||||
from vitrage.datasources.alarm_driver_base import AlarmDriverBase
|
||||
from vitrage.datasources.aodh import AODH_DATASOURCE
|
||||
from vitrage.datasources.aodh.properties import AodhEventType
|
||||
from vitrage.datasources.aodh.properties import AodhProperties as AodhProps
|
||||
from vitrage.datasources.aodh.properties import AodhState
|
||||
from vitrage import os_clients
|
||||
from vitrage.utils import datetime as datetime_utils
|
||||
|
||||
LOG = log.getLogger(__name__)
|
||||
|
||||
|
||||
class AodhDriver(AlarmDriverBase):
|
||||
|
||||
def __init__(self, conf):
|
||||
super(AodhDriver, self).__init__()
|
||||
self._client = None
|
||||
self.conf = conf
|
||||
self._init_aodh_event_actions()
|
||||
self._cache_all_alarms()
|
||||
|
||||
@property
|
||||
def client(self):
|
||||
@ -41,12 +48,17 @@ class AodhDriver(AlarmDriverBase):
|
||||
def _alarm_key(self, alarm):
|
||||
return alarm[AodhProps.ALARM_ID]
|
||||
|
||||
def _cache_all_alarms(self):
|
||||
alarms = self._get_alarms()
|
||||
self._filter_and_cache_alarms(alarms,
|
||||
self._filter_get_valid)
|
||||
|
||||
def _get_alarms(self):
|
||||
try:
|
||||
aodh_alarms = self.client.alarms.list()
|
||||
return [self._convert_alarm(alarm) for alarm in aodh_alarms]
|
||||
except Exception as e:
|
||||
LOG.exception("Exception: %s", e)
|
||||
LOG.exception("Failed to get all alarms, Exception: %s", e)
|
||||
return []
|
||||
|
||||
def _is_erroneous(self, alarm):
|
||||
@ -104,7 +116,8 @@ class AodhDriver(AlarmDriverBase):
|
||||
@classmethod
|
||||
def _convert_alarm(cls, alarm):
|
||||
alarm_type = alarm.type
|
||||
if alarm_type == AodhProps.EVENT and _is_vitrage_alarm(alarm):
|
||||
if alarm_type == AodhProps.EVENT and \
|
||||
_is_vitrage_alarm(alarm.event_rule):
|
||||
return cls._convert_vitrage_alarm(alarm)
|
||||
elif alarm_type == AodhProps.EVENT:
|
||||
return cls._convert_event_alarm(alarm)
|
||||
@ -113,6 +126,167 @@ class AodhDriver(AlarmDriverBase):
|
||||
else:
|
||||
LOG.warning('Unsupported Aodh alarm of type %s' % alarm_type)
|
||||
|
||||
@staticmethod
|
||||
def get_event_types():
|
||||
# Add event_types to receive notifications about
|
||||
return [AodhEventType.CREATION,
|
||||
AodhEventType.STATE_TRANSITION,
|
||||
AodhEventType.RULE_CHANGE,
|
||||
AodhEventType.DELETION]
|
||||
|
||||
def enrich_event(self, event, event_type):
|
||||
if event_type in self.actions:
|
||||
entity = self.actions[event_type](event)
|
||||
else:
|
||||
LOG.warning('Unsupported Aodh event type %s' % event_type)
|
||||
return None
|
||||
|
||||
# Don't need to update entity, only update the cache
|
||||
if entity is None:
|
||||
return None
|
||||
|
||||
entity[DSProps.EVENT_TYPE] = event_type
|
||||
|
||||
return AodhDriver.make_pickleable([entity],
|
||||
AODH_DATASOURCE,
|
||||
DatasourceAction.UPDATE)[0]
|
||||
|
||||
def _init_aodh_event_actions(self):
|
||||
self.actions = {
|
||||
AodhEventType.CREATION: self._convert_alarm_creation_event,
|
||||
AodhEventType.RULE_CHANGE: self._convert_alarm_rule_change_event,
|
||||
AodhEventType.STATE_TRANSITION:
|
||||
self._convert_alarm_state_transition_event,
|
||||
AodhEventType.DELETION: self._convert_alarm_deletion_event
|
||||
}
|
||||
|
||||
@classmethod
|
||||
def _convert_base_event(cls, event):
|
||||
return {
|
||||
AodhProps.PROJECT_ID: event[AodhProps.PROJECT_ID],
|
||||
AodhProps.ALARM_ID: event[AodhProps.ALARM_ID],
|
||||
AodhProps.SEVERITY: event[AodhProps.SEVERITY],
|
||||
AodhProps.TIMESTAMP: event[AodhProps.TIMESTAMP],
|
||||
}
|
||||
|
||||
@classmethod
|
||||
def _convert_vitrage_alarm_event(cls, rule):
|
||||
return {
|
||||
AodhProps.VITRAGE_ID: _parse_query(rule, AodhProps.VITRAGE_ID),
|
||||
AodhProps.RESOURCE_ID: _parse_query(rule, AodhProps.RESOURCE_ID)
|
||||
}
|
||||
|
||||
@classmethod
|
||||
def _convert_threshold_alarm_event(cls, event):
|
||||
rule = event[AodhProps.DETAIL][AodhProps.RULE]
|
||||
return {
|
||||
AodhProps.RESOURCE_ID: _parse_query(rule, AodhProps.RESOURCE_ID),
|
||||
AodhProps.STATE_TIMESTAMP: event[AodhProps.STATE_TIMESTAMP]
|
||||
}
|
||||
|
||||
@classmethod
|
||||
def _convert_event_alarm_event(cls, rule):
|
||||
return {
|
||||
AodhProps.EVENT_TYPE: rule[AodhProps.EVENT_TYPE],
|
||||
AodhProps.RESOURCE_ID:
|
||||
_parse_query(rule, AodhProps.EVENT_RESOURCE_ID)
|
||||
}
|
||||
|
||||
@classmethod
|
||||
def _convert_detail_event(cls, event):
|
||||
alarm_info = event[AodhProps.DETAIL]
|
||||
alarm_rule = alarm_info[AodhProps.RULE]
|
||||
|
||||
entity_detail = {
|
||||
AodhProps.DESCRIPTION: alarm_info[AodhProps.DESCRIPTION],
|
||||
AodhProps.ENABLED: alarm_info[AodhProps.ENABLED],
|
||||
AodhProps.NAME: alarm_info[AodhProps.NAME],
|
||||
AodhProps.STATE: alarm_info[AodhProps.STATE],
|
||||
AodhProps.REPEAT_ACTIONS: alarm_info[AodhProps.REPEAT_ACTIONS],
|
||||
AodhProps.TYPE: alarm_info[AodhProps.TYPE]
|
||||
}
|
||||
|
||||
if _is_vitrage_alarm(alarm_rule):
|
||||
entity_detail.update(cls._convert_vitrage_alarm_event(alarm_rule))
|
||||
elif entity_detail[AodhProps.TYPE] == AodhProps.EVENT:
|
||||
entity_detail.update(cls._convert_event_alarm_event(alarm_rule))
|
||||
elif entity_detail[AodhProps.TYPE] == AodhProps.THRESHOLD:
|
||||
entity_detail.update(
|
||||
cls._convert_threshold_alarm_event(event))
|
||||
|
||||
return entity_detail
|
||||
|
||||
@classmethod
|
||||
def _parse_changed_rule(cls, change_rule):
|
||||
entity = {}
|
||||
if AodhProps.EVENT_TYPE in change_rule:
|
||||
entity[AodhProps.EVENT_TYPE] = change_rule[AodhProps.EVENT_TYPE]
|
||||
if 'query' in change_rule:
|
||||
event_resource_id = \
|
||||
_parse_query(change_rule, AodhProps.EVENT_RESOURCE_ID)
|
||||
resource_id = \
|
||||
_parse_query(change_rule, AodhProps.RESOURCE_ID)
|
||||
if event_resource_id or resource_id:
|
||||
entity[AodhProps.RESOURCE_ID] = event_resource_id if \
|
||||
event_resource_id is not None else resource_id
|
||||
|
||||
return entity
|
||||
|
||||
def _convert_alarm_creation_event(self, event):
|
||||
entity = self._convert_base_event(event)
|
||||
detail = self._convert_detail_event(event)
|
||||
entity.update(detail)
|
||||
|
||||
return self._filter_and_cache_alarm(entity, None,
|
||||
self._filter_get_erroneous,
|
||||
datetime_utils.utcnow(False))
|
||||
|
||||
def _convert_alarm_rule_change_event(self, event):
|
||||
"""handle alarm rule change notification
|
||||
|
||||
example of changed rule:
|
||||
"detail": {"severity": "critical",
|
||||
"rule":
|
||||
{"query": [{"field": "traits.resource_id",
|
||||
"type": "",
|
||||
"value": "1",
|
||||
"op": "eq"}],
|
||||
"event_type": "instance.update"}}
|
||||
"""
|
||||
|
||||
alarm_key = self._alarm_key(event)
|
||||
old_alarm = self.cache.get(alarm_key, (None, None))[0]
|
||||
entity = old_alarm.copy()
|
||||
|
||||
changed_rule = event[AodhProps.DETAIL]
|
||||
for (changed_type, changed_info) in changed_rule.items():
|
||||
# handle changed rule which may effect the neighbor
|
||||
if changed_type == AodhProps.RULE:
|
||||
entity.update(self._parse_changed_rule(
|
||||
changed_rule[changed_type]))
|
||||
# handle other changed alarm properties
|
||||
elif changed_type in AodhProps.__dict__.values():
|
||||
entity[changed_type] = changed_info
|
||||
|
||||
return self._filter_and_cache_alarm(entity, old_alarm,
|
||||
self._filter_get_erroneous,
|
||||
datetime_utils.utcnow(False))
|
||||
|
||||
def _convert_alarm_state_transition_event(self, event):
|
||||
alarm_key = self._alarm_key(event)
|
||||
old_alarm = self.cache.get(alarm_key, (None, None))[0]
|
||||
entity = old_alarm.copy()
|
||||
entity[AodhProps.STATE] = event[AodhProps.DETAIL][AodhProps.STATE]
|
||||
|
||||
return self._filter_and_cache_alarm(entity, old_alarm,
|
||||
self._filter_get_change,
|
||||
datetime_utils.utcnow(False))
|
||||
|
||||
def _convert_alarm_deletion_event(self, event):
|
||||
alarm_key = self._alarm_key(event)
|
||||
alarm = self.cache.pop(alarm_key)[0]
|
||||
return alarm if self._is_erroneous(alarm) else None
|
||||
|
||||
|
||||
def _parse_query(data, key):
|
||||
query_fields = data.get(AodhProps.QUERY, {})
|
||||
@ -123,5 +297,5 @@ def _parse_query(data, key):
|
||||
return None
|
||||
|
||||
|
||||
def _is_vitrage_alarm(alarm):
|
||||
return _parse_query(alarm.event_rule, AodhProps.VITRAGE_ID) is not None
|
||||
def _is_vitrage_alarm(rule):
|
||||
return _parse_query(rule, AodhProps.VITRAGE_ID) is not None
|
||||
|
@ -32,9 +32,18 @@ class AodhProperties(object):
|
||||
TIMESTAMP = 'timestamp'
|
||||
TYPE = 'type'
|
||||
VITRAGE_ID = 'vitrage_id'
|
||||
DETAIL = 'detail'
|
||||
RULE = 'rule'
|
||||
|
||||
|
||||
class AodhState(object):
|
||||
OK = 'ok'
|
||||
ALARM = 'alarm'
|
||||
INSUFFICIENT_DATA = 'insufficient_data'
|
||||
|
||||
|
||||
class AodhEventType(object):
|
||||
CREATION = 'alarm.creation'
|
||||
RULE_CHANGE = 'alarm.rule_change'
|
||||
STATE_TRANSITION = 'alarm.state_transition'
|
||||
DELETION = 'alarm.deletion'
|
||||
|
@ -15,9 +15,11 @@
|
||||
from vitrage.common.constants import DatasourceProperties as DSProps
|
||||
from vitrage.common.constants import EdgeLabel
|
||||
from vitrage.common.constants import EntityCategory
|
||||
from vitrage.common.constants import GraphAction
|
||||
from vitrage.common.constants import VertexProperties as VProps
|
||||
from vitrage.datasources.alarm_transformer_base import AlarmTransformerBase
|
||||
from vitrage.datasources.aodh import AODH_DATASOURCE
|
||||
from vitrage.datasources.aodh.properties import AodhEventType
|
||||
from vitrage.datasources.aodh.properties import AodhProperties as AodhProps
|
||||
from vitrage.datasources.aodh.properties import AodhState
|
||||
from vitrage.datasources import transformer_base as tbase
|
||||
@ -28,6 +30,11 @@ from vitrage.utils import datetime as datetime_utils
|
||||
|
||||
class AodhTransformer(AlarmTransformerBase):
|
||||
|
||||
# Event types which need to refer them differently
|
||||
GRAPH_ACTION_MAPPING = {
|
||||
AodhEventType.DELETION: GraphAction.DELETE_ENTITY,
|
||||
}
|
||||
|
||||
def __init__(self, transformers, conf):
|
||||
super(AodhTransformer, self).__init__(transformers, conf)
|
||||
|
||||
|
@ -95,5 +95,6 @@ class NotificationsEndpoint(object):
|
||||
|
||||
def _enqueue_events(self, enriched_events):
|
||||
for event in enriched_events:
|
||||
self.enqueue_callback(event)
|
||||
LOG.debug('EVENT ENQUEUED: \n' + str(event))
|
||||
if event is not None:
|
||||
self.enqueue_callback(event)
|
||||
LOG.debug('EVENT ENQUEUED: \n' + str(event))
|
||||
|
@ -212,7 +212,6 @@ class TransformerBase(object):
|
||||
:return: the action that the processor needs to perform
|
||||
:rtype: str
|
||||
"""
|
||||
|
||||
if DSProps.EVENT_TYPE in entity_event and \
|
||||
entity_event[DSProps.EVENT_TYPE] in GraphAction.__dict__.values():
|
||||
return entity_event[DSProps.EVENT_TYPE]
|
||||
|
@ -19,7 +19,6 @@ from six.moves import queue
|
||||
from vitrage.common.constants import DatasourceAction
|
||||
from vitrage.common.constants import DatasourceProperties as DSProps
|
||||
from vitrage.common.constants import EdgeProperties as EProps
|
||||
from vitrage.common.constants import GraphAction
|
||||
from vitrage.common.constants import VertexProperties as VProps
|
||||
from vitrage.datasources.nova.host import NOVA_HOST_DATASOURCE
|
||||
from vitrage.evaluator.scenario_evaluator import ScenarioEvaluator
|
||||
@ -239,7 +238,6 @@ class TestScenarioEvaluator(TestFunctionalBase):
|
||||
|
||||
# remove WARNING nagios alarm, leaving only CRITICAL one
|
||||
warning_test['status'] = 'OK'
|
||||
warning_test[DSProps.EVENT_TYPE] = GraphAction.DELETE_ENTITY
|
||||
host_v = self.get_host_after_event(event_queue, warning_test,
|
||||
processor, _TARGET_HOST)
|
||||
alarms = \
|
||||
@ -251,7 +249,6 @@ class TestScenarioEvaluator(TestFunctionalBase):
|
||||
|
||||
# next disable the alarm
|
||||
critical_test['status'] = 'OK'
|
||||
critical_test[DSProps.EVENT_TYPE] = GraphAction.DELETE_ENTITY
|
||||
host_v = self.get_host_after_event(event_queue, critical_test,
|
||||
processor, _TARGET_HOST)
|
||||
alarms = \
|
||||
|
@ -435,3 +435,34 @@ def simple_zabbix_alarm_generators(host_num,
|
||||
})
|
||||
|
||||
return tg.get_trace_generators(test_entity_spec_list)
|
||||
|
||||
|
||||
def simple_aodh_alarm_notification_generators(alarm_num,
|
||||
update_events=0,
|
||||
update_vals=None):
|
||||
"""A function for returning aodh alarm event generators.
|
||||
|
||||
Returns generators for a given number of Aodh alarms.
|
||||
|
||||
:param alarm_num: number of alarms
|
||||
:param update_events: number of update alarms
|
||||
:param update_vals: preset vals for ALL update events
|
||||
:return: generators for alarm_num zones as specified
|
||||
|
||||
Returns generators for a given number of alarms and
|
||||
instances.
|
||||
"""
|
||||
|
||||
alarms = ['alarm-{0}'.format(index) for index in range(alarm_num)]
|
||||
|
||||
test_entity_spec_list = [
|
||||
{tg.DYNAMIC_INFO_FKEY: tg.DRIVER_AODH_UPDATE_D,
|
||||
tg.STATIC_INFO_FKEY: None,
|
||||
tg.MAPPING_KEY: alarms,
|
||||
tg.EXTERNAL_INFO_KEY: update_vals,
|
||||
tg.NAME_KEY: 'Aodh update generator',
|
||||
tg.NUM_EVENTS: update_events
|
||||
}
|
||||
]
|
||||
|
||||
return tg.get_trace_generators(test_entity_spec_list)
|
||||
|
@ -158,3 +158,33 @@ def simple_aodh_alarm_generators(alarm_num,
|
||||
}
|
||||
]
|
||||
return tg.get_trace_generators(test_entity_spec_list)
|
||||
|
||||
|
||||
def simple_aodh_update_alarm_generators(alarm_num,
|
||||
update_events=0,
|
||||
update_vals=None):
|
||||
"""A simple function for returning aodh alarm generators.
|
||||
|
||||
Returns generators for a given number of alarms.
|
||||
|
||||
:param alarm_num: number of alarms
|
||||
:param update_events: number of update events
|
||||
:param update_vals: values of update
|
||||
:return: generators for alarm_num alarms as specified
|
||||
"""
|
||||
|
||||
mapping = [('alarm-{0}'.format(ind), 'resource-{0}'.format(ind))
|
||||
for ind in range(alarm_num)
|
||||
]
|
||||
|
||||
test_entity_spec_list = [
|
||||
{tg.DYNAMIC_INFO_FKEY: tg.TRANS_AODH_UPDATE_D,
|
||||
tg.DYNAMIC_INFO_FPATH: tg.MOCK_TRANSFORMER_PATH,
|
||||
tg.STATIC_INFO_FKEY: None,
|
||||
tg.MAPPING_KEY: mapping,
|
||||
tg.EXTERNAL_INFO_KEY: update_vals,
|
||||
tg.NAME_KEY: 'Aodh update generator',
|
||||
tg.NUM_EVENTS: update_events
|
||||
}
|
||||
]
|
||||
return tg.get_trace_generators(test_entity_spec_list)
|
||||
|
@ -44,6 +44,7 @@ GENERATOR = 'generator'
|
||||
# Mock driver specs
|
||||
MOCK_DRIVER_PATH = '%s/mock_configurations/driver' % \
|
||||
utils.get_resources_dir()
|
||||
DRIVER_AODH_UPDATE_D = 'driver_aodh_update_dynamic.json'
|
||||
DRIVER_HOST_SNAPSHOT_D = 'driver_host_snapshot_dynamic.json'
|
||||
DRIVER_INST_SNAPSHOT_D = 'driver_inst_snapshot_dynamic.json'
|
||||
DRIVER_INST_SNAPSHOT_S = 'driver_inst_snapshot_static.json'
|
||||
@ -64,6 +65,7 @@ DRIVER_ZONE_SNAPSHOT_D = 'driver_zone_snapshot_dynamic.json'
|
||||
MOCK_TRANSFORMER_PATH = '%s/mock_configurations/transformer' % \
|
||||
utils.get_resources_dir()
|
||||
TRANS_AODH_SNAPSHOT_D = 'transformer_aodh_snapshot_dynamic.json'
|
||||
TRANS_AODH_UPDATE_D = 'transformer_aodh_update_dynamic.json'
|
||||
TRANS_INST_SNAPSHOT_D = 'transformer_inst_snapshot_dynamic.json'
|
||||
TRANS_INST_SNAPSHOT_S = 'transformer_inst_snapshot_static.json'
|
||||
TRANS_HOST_SNAPSHOT_D = 'transformer_host_snapshot_dynamic.json'
|
||||
@ -105,7 +107,8 @@ class EventTraceGenerator(object):
|
||||
"""
|
||||
|
||||
static_info_parsers = \
|
||||
{DRIVER_INST_SNAPSHOT_D: _get_vm_snapshot_driver_values,
|
||||
{DRIVER_AODH_UPDATE_D: _get_aodh_alarm_update_driver_values,
|
||||
DRIVER_INST_SNAPSHOT_D: _get_vm_snapshot_driver_values,
|
||||
DRIVER_INST_UPDATE_D: _get_vm_update_driver_values,
|
||||
DRIVER_HOST_SNAPSHOT_D: _get_host_snapshot_driver_values,
|
||||
DRIVER_ZONE_SNAPSHOT_D: _get_zone_snapshot_driver_values,
|
||||
@ -120,6 +123,7 @@ class EventTraceGenerator(object):
|
||||
_get_consistency_update_driver_values,
|
||||
|
||||
TRANS_AODH_SNAPSHOT_D: _get_trans_aodh_alarm_snapshot_values,
|
||||
TRANS_AODH_UPDATE_D: _get_trans_aodh_alarm_snapshot_values,
|
||||
TRANS_INST_SNAPSHOT_D: _get_trans_vm_snapshot_values,
|
||||
TRANS_HOST_SNAPSHOT_D: _get_trans_host_snapshot_values,
|
||||
TRANS_ZONE_SNAPSHOT_D: _get_trans_zone_snapshot_values}
|
||||
@ -604,6 +608,19 @@ def _get_trans_aodh_alarm_snapshot_values(spec):
|
||||
return static_values
|
||||
|
||||
|
||||
def _get_aodh_alarm_update_driver_values(spec):
|
||||
alarms = spec[MAPPING_KEY]
|
||||
static_info_re = None
|
||||
if spec[STATIC_INFO_FKEY] is not None:
|
||||
static_info_re = utils.load_specs(spec[STATIC_INFO_FKEY])
|
||||
static_values = []
|
||||
for alarm in alarms:
|
||||
alarm_id = {"alarm_id": alarm}
|
||||
static_values.append(combine_data(
|
||||
static_info_re, alarm_id, spec.get(EXTERNAL_INFO_KEY, None)))
|
||||
return static_values
|
||||
|
||||
|
||||
def combine_data(static_info_re, mapping_info, external_info):
|
||||
if external_info:
|
||||
mapping_info = utils.merge_vals(mapping_info, external_info)
|
||||
|
@ -0,0 +1,11 @@
|
||||
{
|
||||
"on_behalf_of": "c365d18fcc03493187016ae743f0cc4d",
|
||||
"user_id": "3b0fd6ce33f24e38a4f415c380245e96",
|
||||
"severity": "low",
|
||||
"event_id": "e47602a8-cecd-4dd2-b50c-0f54ebca642a",
|
||||
"timestamp": "2016-11-29T08:05:37.877253",
|
||||
"alarm_id": "alarm-0",
|
||||
"project_id": "c365d18fcc03493187016ae743f0cc4d",
|
||||
"type": "",
|
||||
"detail": {}
|
||||
}
|
@ -0,0 +1,37 @@
|
||||
{
|
||||
"alarm_id": "21127fbd-7633-494e-8e5c-fdf415cdec0c",
|
||||
"description": "test",
|
||||
"enabled": "True",
|
||||
"name": "test",
|
||||
"project_id": "f0895991f44044ccba8e62b201b70360",
|
||||
"repeat_actions": "False",
|
||||
"severity": "low",
|
||||
"state": "alarm",
|
||||
"timestamp": "2016-11-29T06:31:50.094836",
|
||||
"state_timestamp": "2016-11-11T01:58:36.054090",
|
||||
"type": "event",
|
||||
"event_type": "compute.instance.*",
|
||||
"resource_id": "3dcee183-ca42-4ccb-84af-9f0196b2e160",
|
||||
"vitrage_event_type": "alarm.creation",
|
||||
"vitrage_entity_type": "aodh",
|
||||
"vitrage_datasource_action": "update",
|
||||
"vitrage_sample_date": "2016-11-29T06:31:50.094836",
|
||||
"graph_query_result": [
|
||||
{
|
||||
"category": "RESOURCE",
|
||||
"is_placeholder": false,
|
||||
"is_deleted": false,
|
||||
"name": "test",
|
||||
"update_timestamp": "2016-12-02 07:18:05.628479+00:00",
|
||||
"sample_timestamp": "2016-12-02 07:18:05.628479+00:00",
|
||||
"operational_state": "OK",
|
||||
"aggregated_state": "ACTIVE",
|
||||
"state": "ACTIVE",
|
||||
"graph_index": 3,
|
||||
"project_id": "f0895991f44044ccba8e62b201b70360",
|
||||
"type": "nova.instance",
|
||||
"id": "3dcee183-ca42-4ccb-84af-9f0196b2e160"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
@ -0,0 +1,114 @@
|
||||
# Copyright 2016 - ZTE, 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 DatasourceAction
|
||||
from vitrage.common.constants import DatasourceProperties as DSProps
|
||||
from vitrage.common.constants import EdgeLabel
|
||||
from vitrage.common.constants import EntityCategory
|
||||
from vitrage.common.constants import GraphAction
|
||||
from vitrage.common.constants import VertexProperties as VProps
|
||||
from vitrage.datasources.alarm_properties import AlarmProperties as AlarmProps
|
||||
from vitrage.datasources.aodh.properties import AodhProperties as AodhProps
|
||||
from vitrage.datasources.aodh.properties import AodhState
|
||||
from vitrage.datasources.nova.instance import NOVA_INSTANCE_DATASOURCE
|
||||
from vitrage.datasources.transformer_base import TransformerBase
|
||||
from vitrage.graph.driver.elements import Vertex
|
||||
from vitrage.tests import base
|
||||
|
||||
|
||||
class AodhTransformerBaseTest(base.BaseTest):
|
||||
|
||||
def _validate_aodh_vertex_props(self, vertex, event):
|
||||
|
||||
self.assertEqual(EntityCategory.ALARM, vertex[VProps.CATEGORY])
|
||||
self.assertEqual(event[DSProps.ENTITY_TYPE], vertex[VProps.TYPE])
|
||||
self.assertEqual(event[AodhProps.NAME], vertex[VProps.NAME])
|
||||
self.assertEqual(event[AodhProps.SEVERITY], vertex[VProps.SEVERITY])
|
||||
self.assertEqual(event[AodhProps.DESCRIPTION],
|
||||
vertex[AodhProps.DESCRIPTION])
|
||||
self.assertEqual(event[AodhProps.ENABLED], vertex[AodhProps.ENABLED])
|
||||
self.assertEqual(event[AodhProps.PROJECT_ID],
|
||||
vertex[VProps.PROJECT_ID])
|
||||
self.assertEqual(event[AodhProps.REPEAT_ACTIONS],
|
||||
vertex[AodhProps.REPEAT_ACTIONS])
|
||||
self.assertEqual(event[AodhProps.TYPE], vertex['alarm_type'])
|
||||
if event[AodhProps.TYPE] == AodhProps.EVENT:
|
||||
self.assertEqual(event[AodhProps.EVENT_TYPE],
|
||||
vertex[AodhProps.EVENT_TYPE])
|
||||
elif event[AodhProps.TYPE] == AodhProps.THRESHOLD:
|
||||
self.assertEqual(event[AodhProps.STATE_TIMESTAMP],
|
||||
vertex[AodhProps.STATE_TIMESTAMP])
|
||||
self.assertEqual(event[DSProps.SAMPLE_DATE],
|
||||
vertex[VProps.SAMPLE_TIMESTAMP])
|
||||
|
||||
event_status = event[AodhProps.STATE]
|
||||
if event_status == AodhState.OK:
|
||||
self.assertEqual(AlarmProps.INACTIVE_STATE,
|
||||
vertex[VProps.STATE])
|
||||
else:
|
||||
self.assertEqual(AlarmProps.ACTIVE_STATE,
|
||||
vertex[VProps.STATE])
|
||||
self.assertFalse(vertex[VProps.IS_PLACEHOLDER])
|
||||
self.assertFalse(vertex[VProps.IS_DELETED])
|
||||
|
||||
def _validate_action(self, alarm, wrapper):
|
||||
if DSProps.EVENT_TYPE in alarm \
|
||||
and alarm[DSProps.EVENT_TYPE] in GraphAction.__dict__.values():
|
||||
self.assertEqual(alarm[DSProps.EVENT_TYPE], wrapper.action)
|
||||
return
|
||||
|
||||
ds_action = alarm[DSProps.DATASOURCE_ACTION]
|
||||
if ds_action in (DatasourceAction.SNAPSHOT, DatasourceAction.UPDATE):
|
||||
self.assertEqual(GraphAction.UPDATE_ENTITY, wrapper.action)
|
||||
else:
|
||||
self.assertEqual(GraphAction.CREATE_ENTITY, wrapper.action)
|
||||
|
||||
def _validate_neighbors(self, neighbors, alarm_id, event):
|
||||
resource_counter = 0
|
||||
|
||||
for neighbor in neighbors:
|
||||
resource_id = event[AodhProps.RESOURCE_ID]
|
||||
self._validate_instance_neighbor(neighbor,
|
||||
resource_id,
|
||||
alarm_id)
|
||||
resource_counter += 1
|
||||
|
||||
self.assertEqual(1,
|
||||
resource_counter,
|
||||
'Alarm can be belonged to only one resource')
|
||||
|
||||
def _validate_instance_neighbor(self,
|
||||
alarm_neighbor,
|
||||
resource_id,
|
||||
alarm_vertex_id):
|
||||
# validate neighbor vertex
|
||||
self.assertEqual(EntityCategory.RESOURCE,
|
||||
alarm_neighbor.vertex[VProps.CATEGORY])
|
||||
self.assertEqual(NOVA_INSTANCE_DATASOURCE,
|
||||
alarm_neighbor.vertex[VProps.TYPE])
|
||||
self.assertEqual(resource_id, alarm_neighbor.vertex[VProps.ID])
|
||||
self.assertFalse(alarm_neighbor.vertex[VProps.IS_PLACEHOLDER])
|
||||
self.assertFalse(alarm_neighbor.vertex[VProps.IS_DELETED])
|
||||
|
||||
# Validate neighbor edge
|
||||
edge = alarm_neighbor.edge
|
||||
self.assertEqual(edge.target_id, alarm_neighbor.vertex.vertex_id)
|
||||
self.assertEqual(edge.source_id, alarm_vertex_id)
|
||||
self.assertEqual(edge.label, EdgeLabel.ON)
|
||||
|
||||
def _convert_dist_to_vertex(self, neighbor):
|
||||
ver_id = neighbor[VProps.CATEGORY] + \
|
||||
TransformerBase.KEY_SEPARATOR + neighbor[VProps.TYPE] + \
|
||||
TransformerBase.KEY_SEPARATOR + neighbor[VProps.ID]
|
||||
return Vertex(vertex_id=ver_id, properties=neighbor)
|
27
vitrage/tests/unit/datasources/aodh/mock_driver.py
Normal file
27
vitrage/tests/unit/datasources/aodh/mock_driver.py
Normal file
@ -0,0 +1,27 @@
|
||||
# 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.datasources.aodh.driver import AodhDriver
|
||||
|
||||
|
||||
class MockAodhDriver(AodhDriver):
|
||||
"""A aodh driver for tests.
|
||||
|
||||
"""
|
||||
|
||||
def __init__(self, conf):
|
||||
super(MockAodhDriver, self).__init__(conf)
|
||||
|
||||
def _cache_all_alarms(self):
|
||||
pass
|
222
vitrage/tests/unit/datasources/aodh/test_aodh_driver.py
Normal file
222
vitrage/tests/unit/datasources/aodh/test_aodh_driver.py
Normal file
@ -0,0 +1,222 @@
|
||||
# Copyright 2016 - ZTE, 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_config import cfg
|
||||
from oslo_log import log as logging
|
||||
|
||||
from vitrage.common.constants import DatasourceProperties as DSProps
|
||||
from vitrage.common.constants import UpdateMethod
|
||||
from vitrage.datasources.aodh import AODH_DATASOURCE
|
||||
from vitrage.datasources.aodh.properties import AodhEventType
|
||||
from vitrage.datasources.aodh.properties import AodhProperties as AodhProps
|
||||
from vitrage.tests import base
|
||||
from vitrage.tests.mocks import mock_driver
|
||||
from vitrage.tests.unit.datasources.aodh.mock_driver import MockAodhDriver
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class AodhDriverTest(base.BaseTest):
|
||||
|
||||
OPTS = [
|
||||
cfg.StrOpt('update_method',
|
||||
default=UpdateMethod.PUSH),
|
||||
]
|
||||
|
||||
# noinspection PyPep8Naming
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
cls.conf = cfg.ConfigOpts()
|
||||
cls.conf.register_opts(cls.OPTS, group=AODH_DATASOURCE)
|
||||
|
||||
def test_enrich_event(self):
|
||||
|
||||
aodh_driver = MockAodhDriver(self.conf)
|
||||
|
||||
# 1. alarm creation with 'ok' state
|
||||
# prepare data
|
||||
detail_data = {"type": "creation",
|
||||
AodhProps.DETAIL: self._extract_alarm_data()}
|
||||
generators = \
|
||||
mock_driver.simple_aodh_alarm_notification_generators(
|
||||
alarm_num=1,
|
||||
update_events=1,
|
||||
update_vals=detail_data)
|
||||
alarm = mock_driver.generate_sequential_events_list(generators)[0]
|
||||
alarm_info = alarm.copy()
|
||||
|
||||
# action
|
||||
entity = aodh_driver.enrich_event(alarm, AodhEventType.CREATION)
|
||||
|
||||
# Test assertions
|
||||
# alarm with status OK should not be handled
|
||||
self.assertIsNone(entity)
|
||||
|
||||
# 2.alarm state transition from 'ok' to 'alarm'
|
||||
detail_data = {"type": "state transition",
|
||||
AodhProps.DETAIL: {AodhProps.STATE: "alarm"}}
|
||||
alarm.update(detail_data)
|
||||
entity = aodh_driver.enrich_event(alarm,
|
||||
AodhEventType.STATE_TRANSITION)
|
||||
|
||||
# Test assertions
|
||||
# alarm state change: ok->alarm, need to be added
|
||||
self.assertIsNotNone(entity)
|
||||
self._validate_aodh_entity_comm_props(entity, alarm_info)
|
||||
self.assertEqual(entity[AodhProps.STATE],
|
||||
alarm[AodhProps.DETAIL][AodhProps.STATE])
|
||||
self.assertEqual(entity[AodhProps.SEVERITY],
|
||||
alarm[AodhProps.SEVERITY])
|
||||
self.assertEqual(entity[DSProps.EVENT_TYPE],
|
||||
AodhEventType.STATE_TRANSITION)
|
||||
|
||||
# 3. delete alarm which is 'alarm' state
|
||||
# prepare data
|
||||
detail_data = {"type": "deletion"}
|
||||
alarm.update(detail_data)
|
||||
|
||||
# action
|
||||
entity = aodh_driver.enrich_event(alarm, AodhEventType.DELETION)
|
||||
|
||||
# Test assertions
|
||||
self.assertIsNotNone(entity)
|
||||
self._validate_aodh_entity_comm_props(entity, alarm_info)
|
||||
self.assertEqual(entity[DSProps.EVENT_TYPE],
|
||||
AodhEventType.DELETION)
|
||||
|
||||
# 4. alarm creation with 'alarm' state
|
||||
# prepare data
|
||||
detail_data = {"type": "creation",
|
||||
AodhProps.DETAIL:
|
||||
self._extract_alarm_data(state="alarm")}
|
||||
generators = \
|
||||
mock_driver.simple_aodh_alarm_notification_generators(
|
||||
alarm_num=1,
|
||||
update_events=1,
|
||||
update_vals=detail_data)
|
||||
alarm = mock_driver.generate_sequential_events_list(generators)[0]
|
||||
alarm_info = alarm.copy()
|
||||
|
||||
# action
|
||||
entity = aodh_driver.enrich_event(alarm, AodhEventType.CREATION)
|
||||
|
||||
# Test assertions
|
||||
# alarm with status 'alarm' need to be added
|
||||
self.assertIsNotNone(entity)
|
||||
self._validate_aodh_entity_comm_props(entity, alarm_info)
|
||||
self.assertEqual(entity[AodhProps.STATE],
|
||||
alarm[AodhProps.DETAIL][AodhProps.STATE])
|
||||
self.assertEqual(entity[AodhProps.SEVERITY],
|
||||
alarm[AodhProps.SEVERITY])
|
||||
self.assertIsNone(entity[AodhProps.RESOURCE_ID])
|
||||
self.assertEqual(entity[AodhProps.EVENT_TYPE], "*")
|
||||
self.assertEqual(entity[DSProps.EVENT_TYPE],
|
||||
AodhEventType.CREATION)
|
||||
|
||||
# 5. alarm rule change
|
||||
# prepare data
|
||||
detail_data = {"type": "rule change",
|
||||
AodhProps.DETAIL: {
|
||||
"severity": "critical",
|
||||
AodhProps.RULE:
|
||||
{"query": [{"field": "traits.resource_id",
|
||||
"type": "",
|
||||
"value": "1",
|
||||
"op": "eq"}],
|
||||
"event_type": "instance.update"}}}
|
||||
alarm.update(detail_data)
|
||||
|
||||
# action
|
||||
entity = aodh_driver.enrich_event(alarm,
|
||||
AodhEventType.RULE_CHANGE)
|
||||
|
||||
# Test assertions
|
||||
# alarm rule change: need to be update
|
||||
self.assertIsNotNone(entity)
|
||||
self._validate_aodh_entity_comm_props(entity, alarm_info)
|
||||
self.assertEqual(entity[AodhProps.SEVERITY],
|
||||
alarm[AodhProps.DETAIL][AodhProps.SEVERITY])
|
||||
self.assertEqual(
|
||||
entity[AodhProps.EVENT_TYPE],
|
||||
alarm[AodhProps.DETAIL][AodhProps.RULE][AodhProps.EVENT_TYPE])
|
||||
self.assertEqual(entity[AodhProps.RESOURCE_ID],
|
||||
"1")
|
||||
self.assertEqual(entity[DSProps.EVENT_TYPE],
|
||||
AodhEventType.RULE_CHANGE)
|
||||
|
||||
# 6. alarm state change from 'alarm' to 'ok'
|
||||
# prepare data
|
||||
detail_data = {"type": "state transition",
|
||||
AodhProps.DETAIL: {AodhProps.STATE: "ok"}}
|
||||
alarm.update(detail_data)
|
||||
|
||||
# action
|
||||
entity = aodh_driver.enrich_event(alarm,
|
||||
AodhEventType.STATE_TRANSITION)
|
||||
|
||||
# Test assertions
|
||||
# alarm state change: alarm->OK, need to be deleted
|
||||
self.assertIsNotNone(entity)
|
||||
self._validate_aodh_entity_comm_props(entity, alarm_info)
|
||||
self.assertEqual(entity[DSProps.EVENT_TYPE],
|
||||
AodhEventType.STATE_TRANSITION)
|
||||
|
||||
# 7. delete alarm which is 'ok' state
|
||||
# prepare data
|
||||
detail_data = {"type": "deletion"}
|
||||
alarm.update(detail_data)
|
||||
|
||||
# action
|
||||
entity = aodh_driver.enrich_event(alarm, AodhEventType.DELETION)
|
||||
|
||||
# Test assertions
|
||||
self.assertIsNone(entity)
|
||||
|
||||
def _extract_alarm_data(self,
|
||||
state="ok",
|
||||
type="event",
|
||||
rule={"query": [],
|
||||
"event_type": "*"}):
|
||||
|
||||
return {AodhProps.DESCRIPTION: "test",
|
||||
AodhProps.TIMESTAMP: "2016-11-09T01:39:13.839584",
|
||||
AodhProps.ENABLED: True,
|
||||
AodhProps.STATE_TIMESTAMP: "2016-11-09T01:39:13.839584",
|
||||
AodhProps.ALARM_ID: "7e5c3754-e2eb-4782-ae00-7da5ded8568b",
|
||||
AodhProps.REPEAT_ACTIONS: False,
|
||||
AodhProps.PROJECT_ID: "c365d18fcc03493187016ae743f0cc4d",
|
||||
AodhProps.NAME: "test",
|
||||
AodhProps.SEVERITY: "low",
|
||||
AodhProps.TYPE: type,
|
||||
AodhProps.STATE: state,
|
||||
AodhProps.RULE: rule}
|
||||
|
||||
def _validate_aodh_entity_comm_props(self, entity, alarm):
|
||||
|
||||
self.assertEqual(entity[AodhProps.ALARM_ID],
|
||||
alarm[AodhProps.ALARM_ID])
|
||||
self.assertEqual(entity[AodhProps.PROJECT_ID],
|
||||
alarm[AodhProps.PROJECT_ID])
|
||||
self.assertEqual(entity[AodhProps.TIMESTAMP],
|
||||
alarm[AodhProps.TIMESTAMP])
|
||||
self.assertEqual(entity[AodhProps.DESCRIPTION],
|
||||
alarm[AodhProps.DETAIL][AodhProps.DESCRIPTION])
|
||||
self.assertEqual(entity[AodhProps.ENABLED],
|
||||
alarm[AodhProps.DETAIL][AodhProps.ENABLED])
|
||||
self.assertEqual(entity[AodhProps.NAME],
|
||||
alarm[AodhProps.DETAIL][AodhProps.NAME])
|
||||
self.assertEqual(entity[AodhProps.REPEAT_ACTIONS],
|
||||
alarm[AodhProps.DETAIL][AodhProps.REPEAT_ACTIONS])
|
||||
self.assertEqual(entity[AodhProps.TYPE],
|
||||
alarm[AodhProps.DETAIL][AodhProps.TYPE])
|
@ -15,32 +15,25 @@
|
||||
from oslo_config import cfg
|
||||
from oslo_log import log as logging
|
||||
|
||||
from vitrage.common.constants import DatasourceAction
|
||||
from vitrage.common.constants import DatasourceProperties as DSProps
|
||||
from vitrage.common.constants import EdgeLabel
|
||||
from vitrage.common.constants import EntityCategory
|
||||
from vitrage.common.constants import GraphAction
|
||||
from vitrage.common.constants import UpdateMethod
|
||||
from vitrage.common.constants import VertexProperties as VProps
|
||||
from vitrage.datasources.alarm_properties import AlarmProperties as AlarmProps
|
||||
from vitrage.datasources.aodh import AODH_DATASOURCE
|
||||
from vitrage.datasources.aodh.properties import AodhProperties as AodhProps
|
||||
from vitrage.datasources.aodh.properties import AodhState
|
||||
from vitrage.datasources.aodh.transformer import AodhTransformer
|
||||
from vitrage.datasources.nova.instance import NOVA_INSTANCE_DATASOURCE
|
||||
from vitrage.datasources.transformer_base import TransformerBase
|
||||
from vitrage.graph.driver.elements import Vertex
|
||||
from vitrage.tests import base
|
||||
from vitrage.tests.mocks import mock_transformer as mock_sync
|
||||
from vitrage.tests.unit.datasources.aodh.aodh_transformer_base_test import \
|
||||
AodhTransformerBaseTest
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class TestAodhAlarmTransformer(base.BaseTest):
|
||||
class TestAodhAlarmTransformer(AodhTransformerBaseTest):
|
||||
|
||||
OPTS = [
|
||||
cfg.StrOpt('update_method',
|
||||
default=UpdateMethod.PUSH),
|
||||
default=UpdateMethod.PULL),
|
||||
]
|
||||
|
||||
@classmethod
|
||||
@ -112,86 +105,49 @@ class TestAodhAlarmTransformer(base.BaseTest):
|
||||
|
||||
self._validate_action(event, wrapper)
|
||||
|
||||
def _validate_aodh_vertex_props(self, vertex, event):
|
||||
|
||||
self.assertEqual(EntityCategory.ALARM, vertex[VProps.CATEGORY])
|
||||
self.assertEqual(event[DSProps.ENTITY_TYPE], vertex[VProps.TYPE])
|
||||
self.assertEqual(event[AodhProps.NAME], vertex[VProps.NAME])
|
||||
self.assertEqual(event[AodhProps.SEVERITY], vertex[VProps.SEVERITY])
|
||||
self.assertEqual(event[AodhProps.DESCRIPTION],
|
||||
vertex[AodhProps.DESCRIPTION])
|
||||
self.assertEqual(event[AodhProps.ENABLED], vertex[AodhProps.ENABLED])
|
||||
self.assertEqual(event[AodhProps.PROJECT_ID],
|
||||
vertex[VProps.PROJECT_ID])
|
||||
self.assertEqual(event[AodhProps.REPEAT_ACTIONS],
|
||||
vertex[AodhProps.REPEAT_ACTIONS])
|
||||
self.assertEqual(event[AodhProps.TYPE], vertex['alarm_type'])
|
||||
if event[AodhProps.TYPE] == AodhProps.EVENT:
|
||||
self.assertEqual(event[AodhProps.EVENT_TYPE],
|
||||
vertex[AodhProps.EVENT_TYPE])
|
||||
elif event[AodhProps.TYPE] == AodhProps.THRESHOLD:
|
||||
self.assertEqual(event[AodhProps.STATE_TIMESTAMP],
|
||||
vertex[AodhProps.STATE_TIMESTAMP])
|
||||
self.assertEqual(event[DSProps.SAMPLE_DATE],
|
||||
vertex[VProps.SAMPLE_TIMESTAMP])
|
||||
class TestAodhAlarmPushTransformer(AodhTransformerBaseTest):
|
||||
|
||||
event_status = event[AodhProps.STATE]
|
||||
if event_status == AodhState.OK:
|
||||
self.assertEqual(AlarmProps.INACTIVE_STATE,
|
||||
vertex[VProps.STATE])
|
||||
else:
|
||||
self.assertEqual(AlarmProps.ACTIVE_STATE,
|
||||
vertex[VProps.STATE])
|
||||
self.assertFalse(vertex[VProps.IS_PLACEHOLDER])
|
||||
self.assertFalse(vertex[VProps.IS_DELETED])
|
||||
OPTS = [
|
||||
cfg.StrOpt('update_method',
|
||||
default=UpdateMethod.PUSH),
|
||||
]
|
||||
|
||||
def _validate_action(self, alarm, wrapper):
|
||||
if DSProps.EVENT_TYPE in alarm \
|
||||
and alarm[DSProps.EVENT_TYPE] in GraphAction.__dict__.values():
|
||||
self.assertEqual(alarm[DSProps.EVENT_TYPE], wrapper.action)
|
||||
return
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
cls.transformers = {}
|
||||
cls.conf = cfg.ConfigOpts()
|
||||
cls.conf.register_opts(cls.OPTS, group=AODH_DATASOURCE)
|
||||
cls.transformers[AODH_DATASOURCE] = \
|
||||
AodhTransformer(cls.transformers, cls.conf)
|
||||
|
||||
ds_action = alarm[DSProps.DATASOURCE_ACTION]
|
||||
if ds_action in (DatasourceAction.SNAPSHOT, DatasourceAction.UPDATE):
|
||||
self.assertEqual(GraphAction.UPDATE_ENTITY, wrapper.action)
|
||||
else:
|
||||
self.assertEqual(GraphAction.CREATE_ENTITY, wrapper.action)
|
||||
def test_update_transform(self):
|
||||
LOG.debug('Aodh update alarm transformer test:'
|
||||
'transform entity event update')
|
||||
|
||||
def _validate_neighbors(self, neighbors, alarm_id, event):
|
||||
resource_counter = 0
|
||||
# Test setup
|
||||
spec_list = \
|
||||
mock_sync.simple_aodh_update_alarm_generators(alarm_num=5,
|
||||
update_events=5)
|
||||
static_events = mock_sync.generate_random_events_list(spec_list)
|
||||
|
||||
for neighbor in neighbors:
|
||||
resource_id = event[AodhProps.RESOURCE_ID]
|
||||
self._validate_instance_neighbor(neighbor,
|
||||
resource_id,
|
||||
alarm_id)
|
||||
resource_counter += 1
|
||||
for event in static_events:
|
||||
# convert neighbor from dict to vertex object
|
||||
neighbors = event[TransformerBase.QUERY_RESULT]
|
||||
vertices = []
|
||||
for neighbor in neighbors:
|
||||
vertices.append(self._convert_dist_to_vertex(neighbor))
|
||||
event[TransformerBase.QUERY_RESULT] = vertices
|
||||
|
||||
self.assertEqual(1,
|
||||
resource_counter,
|
||||
'Alarm can be belonged to only one resource')
|
||||
# Test action
|
||||
wrapper = self.transformers[AODH_DATASOURCE].transform(event)
|
||||
|
||||
def _validate_instance_neighbor(self,
|
||||
alarm_neighbor,
|
||||
resource_id,
|
||||
alarm_vertex_id):
|
||||
# validate neighbor vertex
|
||||
self.assertEqual(EntityCategory.RESOURCE,
|
||||
alarm_neighbor.vertex[VProps.CATEGORY])
|
||||
self.assertEqual(NOVA_INSTANCE_DATASOURCE,
|
||||
alarm_neighbor.vertex[VProps.TYPE])
|
||||
self.assertEqual(resource_id, alarm_neighbor.vertex[VProps.ID])
|
||||
self.assertFalse(alarm_neighbor.vertex[VProps.IS_PLACEHOLDER])
|
||||
self.assertFalse(alarm_neighbor.vertex[VProps.IS_DELETED])
|
||||
# Test assertions
|
||||
vertex = wrapper.vertex
|
||||
self._validate_aodh_vertex_props(vertex, event)
|
||||
|
||||
# Validate neighbor edge
|
||||
edge = alarm_neighbor.edge
|
||||
self.assertEqual(edge.target_id, alarm_neighbor.vertex.vertex_id)
|
||||
self.assertEqual(edge.source_id, alarm_vertex_id)
|
||||
self.assertEqual(edge.label, EdgeLabel.ON)
|
||||
neighbors = wrapper.neighbors
|
||||
self.assertEqual(1, len(neighbors))
|
||||
self._validate_neighbors(neighbors, vertex.vertex_id, event)
|
||||
|
||||
def _convert_dist_to_vertex(self, neighbor):
|
||||
ver_id = neighbor[VProps.CATEGORY] + \
|
||||
TransformerBase.KEY_SEPARATOR + neighbor[VProps.TYPE] + \
|
||||
TransformerBase.KEY_SEPARATOR + neighbor[VProps.ID]
|
||||
return Vertex(vertex_id=ver_id, properties=neighbor)
|
||||
self._validate_action(event, wrapper)
|
||||
|
@ -144,9 +144,6 @@ class NagiosDriverTest(NagiosBaseTest):
|
||||
self.assertEqual(2, len(services))
|
||||
self._assert_contains(service_data1, services)
|
||||
self._assert_contains(service_data2, services)
|
||||
for service in services:
|
||||
self.assertEqual(GraphAction.DELETE_ENTITY,
|
||||
service[DSProps.EVENT_TYPE])
|
||||
|
||||
# Action
|
||||
services = nagios_driver._get_all_alarms()
|
||||
@ -278,9 +275,6 @@ class NagiosDriverTest(NagiosBaseTest):
|
||||
self.assertEqual(2, len(services))
|
||||
self._assert_contains(service_data1, services)
|
||||
self._assert_contains(service_data2, services)
|
||||
for service in services:
|
||||
self.assertEqual(GraphAction.DELETE_ENTITY,
|
||||
service[DSProps.EVENT_TYPE])
|
||||
|
||||
# Action
|
||||
services = nagios_driver._get_changed_alarms()
|
||||
|
@ -90,6 +90,7 @@ class NagiosTransformerTest(base.BaseTest):
|
||||
# Test action
|
||||
wrapper = NagiosTransformer(self.transformers, self.conf).\
|
||||
transform(alarm)
|
||||
|
||||
self._validate_vertex(wrapper.vertex, alarm)
|
||||
|
||||
neighbors = wrapper.neighbors
|
||||
@ -103,14 +104,12 @@ class NagiosTransformerTest(base.BaseTest):
|
||||
self._validate_action(alarm, wrapper)
|
||||
|
||||
def _validate_action(self, alarm, wrapper):
|
||||
if DSProps.EVENT_TYPE in alarm \
|
||||
and alarm[DSProps.EVENT_TYPE] in GraphAction.__dict__.values():
|
||||
self.assertEqual(alarm[DSProps.EVENT_TYPE], wrapper.action)
|
||||
return
|
||||
|
||||
ds_action = alarm[DSProps.DATASOURCE_ACTION]
|
||||
if ds_action in (DatasourceAction.SNAPSHOT, DatasourceAction.UPDATE):
|
||||
self.assertEqual(GraphAction.UPDATE_ENTITY, wrapper.action)
|
||||
if alarm[NagiosProperties.STATUS] == 'OK':
|
||||
self.assertEqual(GraphAction.DELETE_ENTITY, wrapper.action)
|
||||
else:
|
||||
self.assertEqual(GraphAction.UPDATE_ENTITY, wrapper.action)
|
||||
else:
|
||||
self.assertEqual(GraphAction.CREATE_ENTITY, wrapper.action)
|
||||
|
||||
|
@ -143,9 +143,6 @@ class ZabbixDriverTest(ZabbixBaseTest):
|
||||
self.assertEqual(2, len(alarms))
|
||||
self._assert_contains(expected_alarm1, alarms)
|
||||
self._assert_contains(expected_alarm2, alarms)
|
||||
for alarm in alarms:
|
||||
self.assertEqual(GraphAction.DELETE_ENTITY,
|
||||
alarm[DSProps.EVENT_TYPE])
|
||||
|
||||
# Step 4 - get all when all alarms are inactivated and their status
|
||||
# was not changed
|
||||
@ -254,9 +251,6 @@ class ZabbixDriverTest(ZabbixBaseTest):
|
||||
self.assertEqual(2, len(alarms))
|
||||
self._assert_contains(expected_alarm1, alarms)
|
||||
self._assert_contains(expected_alarm2, alarms)
|
||||
for alarm in alarms:
|
||||
self.assertEqual(GraphAction.DELETE_ENTITY,
|
||||
alarm[DSProps.EVENT_TYPE])
|
||||
|
||||
# Step 6 - get changes when no change occurred
|
||||
# Action
|
||||
|
@ -108,14 +108,12 @@ class ZabbixTransformerTest(base.BaseTest):
|
||||
self._validate_action(alarm, wrapper)
|
||||
|
||||
def _validate_action(self, alarm, wrapper):
|
||||
if DSProps.EVENT_TYPE in alarm \
|
||||
and alarm[DSProps.EVENT_TYPE] in GraphAction.__dict__.values():
|
||||
self.assertEqual(alarm[DSProps.EVENT_TYPE], wrapper.action)
|
||||
return
|
||||
|
||||
ds_action = alarm[DSProps.DATASOURCE_ACTION]
|
||||
if ds_action in (DatasourceAction.SNAPSHOT, DatasourceAction.UPDATE):
|
||||
self.assertEqual(GraphAction.UPDATE_ENTITY, wrapper.action)
|
||||
if alarm[ZabbixProps.VALUE] == ZabbixTriggerValue.OK:
|
||||
self.assertEqual(GraphAction.DELETE_ENTITY, wrapper.action)
|
||||
else:
|
||||
self.assertEqual(GraphAction.UPDATE_ENTITY, wrapper.action)
|
||||
else:
|
||||
self.assertEqual(GraphAction.CREATE_ENTITY, wrapper.action)
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user