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:
dongwenjuan 2016-12-12 12:26:12 +08:00
parent c4542eaaa5
commit d57af3234d
24 changed files with 793 additions and 134 deletions

View File

@ -53,4 +53,13 @@ Enabling Vitrage in DevStack
notification_topics = notifications,vitrage_notifications notification_topics = notifications,vitrage_notifications
notification_driver=messagingv2 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``

View File

@ -41,6 +41,7 @@ ENABLED_SERVICES+=,vitrage-api,vitrage-graph
ENABLED_SERVICES+=,key,aodi-api,aodh-notifier,aodh-evaluator ENABLED_SERVICES+=,key,aodi-api,aodh-notifier,aodh-evaluator
ENABLED_SERVICES+=,ceilometer-alarm-evaluator,ceilometer-alarm-notifier ENABLED_SERVICES+=,ceilometer-alarm-evaluator,ceilometer-alarm-notifier
ENABLED_SERVICES+=,ceilometer-api ENABLED_SERVICES+=,ceilometer-api
ENABLED_SERVICES+=,aodh-api
export ENABLED_SERVICES export ENABLED_SERVICES
@ -69,6 +70,11 @@ notification_topics = notifications,vitrage_notifications
notification_driver = messagingv2 notification_driver = messagingv2
policy_file = /etc/heat/policy.json-tempest policy_file = /etc/heat/policy.json-tempest
[[post-config|\$AODH_CONF]]
[oslo_messaging_notifications]
driver = messagingv2
topics = notifications, vitrage_notifications
[[post-config|\$VITRAGE_CONF]] [[post-config|\$VITRAGE_CONF]]
[static_physical] [static_physical]
changes_interval = 5 changes_interval = 5

View File

@ -85,15 +85,16 @@ class AlarmDriverBase(DriverBase):
def _get_all_alarms(self): def _get_all_alarms(self):
alarms = self._get_alarms() alarms = self._get_alarms()
self._enrich_alarms(alarms) self._enrich_alarms(alarms)
return self._filter_and_cache_alarms(alarms, return self._filter_and_cache_alarms(
AlarmDriverBase._filter_get_all) alarms,
self._filter_get_erroneous)
def _get_changed_alarms(self): def _get_changed_alarms(self):
alarms = self._get_alarms() alarms = self._get_alarms()
self._enrich_alarms(alarms) self._enrich_alarms(alarms)
return self._filter_and_cache_alarms( return self._filter_and_cache_alarms(
alarms, alarms,
AlarmDriverBase._filter_get_changes) self._filter_get_change)
def _filter_and_cache_alarms(self, alarms, filter_): def _filter_and_cache_alarms(self, alarms, filter_):
alarms_to_update = [] alarms_to_update = []
@ -101,16 +102,11 @@ class AlarmDriverBase(DriverBase):
for alarm in alarms: for alarm in alarms:
alarm_key = self._alarm_key(alarm) alarm_key = self._alarm_key(alarm)
old_alarm, timestamp = self.cache.get(alarm_key, (None, None)) old_alarm = self.cache.get(alarm_key, (None, None))[0]
if self._filter_and_cache_alarm(
if filter_(self, alarm, old_alarm): alarm, old_alarm, filter_, now):
# delete state changed alarm: alarm->OK
if not self._is_erroneous(alarm):
alarm[DSProps.EVENT_TYPE] = GraphAction.DELETE_ENTITY
alarms_to_update.append(alarm) alarms_to_update.append(alarm)
self.cache[alarm_key] = alarm, now
# add alarms that were deleted # add alarms that were deleted
values = list(self.cache.values()) values = list(self.cache.values())
for cached_alarm, timestamp in values: for cached_alarm, timestamp in values:
@ -122,13 +118,16 @@ class AlarmDriverBase(DriverBase):
return alarms_to_update 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 \ return alarm \
if self._is_valid(alarm) and \ if self._is_valid(alarm) and \
(self._is_erroneous(alarm) or self._is_erroneous(old_alarm)) \ (self._is_erroneous(alarm) or self._is_erroneous(old_alarm)) \
else None else None
def _filter_get_changes(self, alarm, old_alarm): def _filter_get_change(self, alarm, old_alarm):
if not self._is_valid(alarm): if not self._is_valid(alarm):
return None return None
if self._status_changed(alarm, old_alarm): if self._status_changed(alarm, old_alarm):
@ -137,3 +136,8 @@ class AlarmDriverBase(DriverBase):
return alarm return alarm
else: else:
return None 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

View File

@ -14,9 +14,11 @@
from oslo_log import log as logging 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 DatasourceProperties as DSProps
from vitrage.common.constants import EntityCategory from vitrage.common.constants import EntityCategory
from vitrage.common.constants import GraphAction from vitrage.common.constants import GraphAction
from vitrage.common.exception import VitrageTransformerError
from vitrage.datasources.alarm_properties import AlarmProperties as AlarmProps from vitrage.datasources.alarm_properties import AlarmProperties as AlarmProps
from vitrage.datasources import transformer_base as tbase from vitrage.datasources import transformer_base as tbase
@ -31,6 +33,27 @@ class AlarmTransformerBase(tbase.TransformerBase):
def _ok_status(self, entity_event): def _ok_status(self, entity_event):
pass 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): def create_placeholder_vertex(self, **kwargs):
LOG.info('An alarm cannot be a placeholder') LOG.info('An alarm cannot be a placeholder')
pass pass

View File

@ -28,7 +28,7 @@ OPTS = [
help='Aodh driver class path', help='Aodh driver class path',
required=True), required=True),
cfg.StrOpt('update_method', cfg.StrOpt('update_method',
default=UpdateMethod.PULL, default=UpdateMethod.PUSH,
help='None: updates only via Vitrage periodic snapshots.' help='None: updates only via Vitrage periodic snapshots.'
'Pull: updates every [changes_interval] seconds.' 'Pull: updates every [changes_interval] seconds.'
'Push: updates by getting notifications from the' 'Push: updates by getting notifications from the'

View File

@ -14,20 +14,27 @@
from oslo_log import log 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.alarm_driver_base import AlarmDriverBase
from vitrage.datasources.aodh import AODH_DATASOURCE 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 AodhProperties as AodhProps
from vitrage.datasources.aodh.properties import AodhState from vitrage.datasources.aodh.properties import AodhState
from vitrage import os_clients from vitrage import os_clients
from vitrage.utils import datetime as datetime_utils
LOG = log.getLogger(__name__) LOG = log.getLogger(__name__)
class AodhDriver(AlarmDriverBase): class AodhDriver(AlarmDriverBase):
def __init__(self, conf): def __init__(self, conf):
super(AodhDriver, self).__init__() super(AodhDriver, self).__init__()
self._client = None self._client = None
self.conf = conf self.conf = conf
self._init_aodh_event_actions()
self._cache_all_alarms()
@property @property
def client(self): def client(self):
@ -41,12 +48,17 @@ class AodhDriver(AlarmDriverBase):
def _alarm_key(self, alarm): def _alarm_key(self, alarm):
return alarm[AodhProps.ALARM_ID] 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): def _get_alarms(self):
try: try:
aodh_alarms = self.client.alarms.list() aodh_alarms = self.client.alarms.list()
return [self._convert_alarm(alarm) for alarm in aodh_alarms] return [self._convert_alarm(alarm) for alarm in aodh_alarms]
except Exception as e: except Exception as e:
LOG.exception("Exception: %s", e) LOG.exception("Failed to get all alarms, Exception: %s", e)
return [] return []
def _is_erroneous(self, alarm): def _is_erroneous(self, alarm):
@ -104,7 +116,8 @@ class AodhDriver(AlarmDriverBase):
@classmethod @classmethod
def _convert_alarm(cls, alarm): def _convert_alarm(cls, alarm):
alarm_type = alarm.type 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) return cls._convert_vitrage_alarm(alarm)
elif alarm_type == AodhProps.EVENT: elif alarm_type == AodhProps.EVENT:
return cls._convert_event_alarm(alarm) return cls._convert_event_alarm(alarm)
@ -113,6 +126,167 @@ class AodhDriver(AlarmDriverBase):
else: else:
LOG.warning('Unsupported Aodh alarm of type %s' % alarm_type) 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): def _parse_query(data, key):
query_fields = data.get(AodhProps.QUERY, {}) query_fields = data.get(AodhProps.QUERY, {})
@ -123,5 +297,5 @@ def _parse_query(data, key):
return None return None
def _is_vitrage_alarm(alarm): def _is_vitrage_alarm(rule):
return _parse_query(alarm.event_rule, AodhProps.VITRAGE_ID) is not None return _parse_query(rule, AodhProps.VITRAGE_ID) is not None

View File

@ -32,9 +32,18 @@ class AodhProperties(object):
TIMESTAMP = 'timestamp' TIMESTAMP = 'timestamp'
TYPE = 'type' TYPE = 'type'
VITRAGE_ID = 'vitrage_id' VITRAGE_ID = 'vitrage_id'
DETAIL = 'detail'
RULE = 'rule'
class AodhState(object): class AodhState(object):
OK = 'ok' OK = 'ok'
ALARM = 'alarm' ALARM = 'alarm'
INSUFFICIENT_DATA = 'insufficient_data' INSUFFICIENT_DATA = 'insufficient_data'
class AodhEventType(object):
CREATION = 'alarm.creation'
RULE_CHANGE = 'alarm.rule_change'
STATE_TRANSITION = 'alarm.state_transition'
DELETION = 'alarm.deletion'

View File

@ -15,9 +15,11 @@
from vitrage.common.constants import DatasourceProperties as DSProps from vitrage.common.constants import DatasourceProperties as DSProps
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.common.constants import GraphAction
from vitrage.common.constants import VertexProperties as VProps from vitrage.common.constants import VertexProperties as VProps
from vitrage.datasources.alarm_transformer_base import AlarmTransformerBase from vitrage.datasources.alarm_transformer_base import AlarmTransformerBase
from vitrage.datasources.aodh import AODH_DATASOURCE 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 AodhProperties as AodhProps
from vitrage.datasources.aodh.properties import AodhState from vitrage.datasources.aodh.properties import AodhState
from vitrage.datasources import transformer_base as tbase from vitrage.datasources import transformer_base as tbase
@ -28,6 +30,11 @@ from vitrage.utils import datetime as datetime_utils
class AodhTransformer(AlarmTransformerBase): class AodhTransformer(AlarmTransformerBase):
# Event types which need to refer them differently
GRAPH_ACTION_MAPPING = {
AodhEventType.DELETION: GraphAction.DELETE_ENTITY,
}
def __init__(self, transformers, conf): def __init__(self, transformers, conf):
super(AodhTransformer, self).__init__(transformers, conf) super(AodhTransformer, self).__init__(transformers, conf)

View File

@ -95,5 +95,6 @@ class NotificationsEndpoint(object):
def _enqueue_events(self, enriched_events): def _enqueue_events(self, enriched_events):
for event in enriched_events: for event in enriched_events:
if event is not None:
self.enqueue_callback(event) self.enqueue_callback(event)
LOG.debug('EVENT ENQUEUED: \n' + str(event)) LOG.debug('EVENT ENQUEUED: \n' + str(event))

View File

@ -212,7 +212,6 @@ class TransformerBase(object):
:return: the action that the processor needs to perform :return: the action that the processor needs to perform
:rtype: str :rtype: str
""" """
if DSProps.EVENT_TYPE in entity_event and \ if DSProps.EVENT_TYPE in entity_event and \
entity_event[DSProps.EVENT_TYPE] in GraphAction.__dict__.values(): entity_event[DSProps.EVENT_TYPE] in GraphAction.__dict__.values():
return entity_event[DSProps.EVENT_TYPE] return entity_event[DSProps.EVENT_TYPE]

View File

@ -19,7 +19,6 @@ from six.moves import queue
from vitrage.common.constants import DatasourceAction from vitrage.common.constants import DatasourceAction
from vitrage.common.constants import DatasourceProperties as DSProps from vitrage.common.constants import DatasourceProperties as DSProps
from vitrage.common.constants import EdgeProperties as EProps from vitrage.common.constants import EdgeProperties as EProps
from vitrage.common.constants import GraphAction
from vitrage.common.constants import VertexProperties as VProps from vitrage.common.constants import VertexProperties as VProps
from vitrage.datasources.nova.host import NOVA_HOST_DATASOURCE from vitrage.datasources.nova.host import NOVA_HOST_DATASOURCE
from vitrage.evaluator.scenario_evaluator import ScenarioEvaluator from vitrage.evaluator.scenario_evaluator import ScenarioEvaluator
@ -239,7 +238,6 @@ class TestScenarioEvaluator(TestFunctionalBase):
# remove WARNING nagios alarm, leaving only CRITICAL one # remove WARNING nagios alarm, leaving only CRITICAL one
warning_test['status'] = 'OK' warning_test['status'] = 'OK'
warning_test[DSProps.EVENT_TYPE] = GraphAction.DELETE_ENTITY
host_v = self.get_host_after_event(event_queue, warning_test, host_v = self.get_host_after_event(event_queue, warning_test,
processor, _TARGET_HOST) processor, _TARGET_HOST)
alarms = \ alarms = \
@ -251,7 +249,6 @@ class TestScenarioEvaluator(TestFunctionalBase):
# next disable the alarm # next disable the alarm
critical_test['status'] = 'OK' critical_test['status'] = 'OK'
critical_test[DSProps.EVENT_TYPE] = GraphAction.DELETE_ENTITY
host_v = self.get_host_after_event(event_queue, critical_test, host_v = self.get_host_after_event(event_queue, critical_test,
processor, _TARGET_HOST) processor, _TARGET_HOST)
alarms = \ alarms = \

View File

@ -435,3 +435,34 @@ def simple_zabbix_alarm_generators(host_num,
}) })
return tg.get_trace_generators(test_entity_spec_list) 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)

View File

@ -158,3 +158,33 @@ def simple_aodh_alarm_generators(alarm_num,
} }
] ]
return tg.get_trace_generators(test_entity_spec_list) 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)

View File

@ -44,6 +44,7 @@ GENERATOR = 'generator'
# Mock driver specs # Mock driver specs
MOCK_DRIVER_PATH = '%s/mock_configurations/driver' % \ MOCK_DRIVER_PATH = '%s/mock_configurations/driver' % \
utils.get_resources_dir() utils.get_resources_dir()
DRIVER_AODH_UPDATE_D = 'driver_aodh_update_dynamic.json'
DRIVER_HOST_SNAPSHOT_D = 'driver_host_snapshot_dynamic.json' DRIVER_HOST_SNAPSHOT_D = 'driver_host_snapshot_dynamic.json'
DRIVER_INST_SNAPSHOT_D = 'driver_inst_snapshot_dynamic.json' DRIVER_INST_SNAPSHOT_D = 'driver_inst_snapshot_dynamic.json'
DRIVER_INST_SNAPSHOT_S = 'driver_inst_snapshot_static.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' % \ MOCK_TRANSFORMER_PATH = '%s/mock_configurations/transformer' % \
utils.get_resources_dir() utils.get_resources_dir()
TRANS_AODH_SNAPSHOT_D = 'transformer_aodh_snapshot_dynamic.json' 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_D = 'transformer_inst_snapshot_dynamic.json'
TRANS_INST_SNAPSHOT_S = 'transformer_inst_snapshot_static.json' TRANS_INST_SNAPSHOT_S = 'transformer_inst_snapshot_static.json'
TRANS_HOST_SNAPSHOT_D = 'transformer_host_snapshot_dynamic.json' TRANS_HOST_SNAPSHOT_D = 'transformer_host_snapshot_dynamic.json'
@ -105,7 +107,8 @@ class EventTraceGenerator(object):
""" """
static_info_parsers = \ 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_INST_UPDATE_D: _get_vm_update_driver_values,
DRIVER_HOST_SNAPSHOT_D: _get_host_snapshot_driver_values, DRIVER_HOST_SNAPSHOT_D: _get_host_snapshot_driver_values,
DRIVER_ZONE_SNAPSHOT_D: _get_zone_snapshot_driver_values, DRIVER_ZONE_SNAPSHOT_D: _get_zone_snapshot_driver_values,
@ -120,6 +123,7 @@ class EventTraceGenerator(object):
_get_consistency_update_driver_values, _get_consistency_update_driver_values,
TRANS_AODH_SNAPSHOT_D: _get_trans_aodh_alarm_snapshot_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_INST_SNAPSHOT_D: _get_trans_vm_snapshot_values,
TRANS_HOST_SNAPSHOT_D: _get_trans_host_snapshot_values, TRANS_HOST_SNAPSHOT_D: _get_trans_host_snapshot_values,
TRANS_ZONE_SNAPSHOT_D: _get_trans_zone_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 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): def combine_data(static_info_re, mapping_info, external_info):
if external_info: if external_info:
mapping_info = utils.merge_vals(mapping_info, external_info) mapping_info = utils.merge_vals(mapping_info, external_info)

View File

@ -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": {}
}

View File

@ -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"
}
]
}

View File

@ -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)

View 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

View 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])

View File

@ -15,32 +15,25 @@
from oslo_config import cfg from oslo_config import cfg
from oslo_log import log as logging 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 DatasourceProperties as DSProps
from vitrage.common.constants import EdgeLabel
from vitrage.common.constants import EntityCategory from vitrage.common.constants import EntityCategory
from vitrage.common.constants import GraphAction
from vitrage.common.constants import UpdateMethod 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 import AODH_DATASOURCE
from vitrage.datasources.aodh.properties import AodhProperties as AodhProps 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.aodh.transformer import AodhTransformer
from vitrage.datasources.nova.instance import NOVA_INSTANCE_DATASOURCE
from vitrage.datasources.transformer_base import TransformerBase 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.mocks import mock_transformer as mock_sync
from vitrage.tests.unit.datasources.aodh.aodh_transformer_base_test import \
AodhTransformerBaseTest
LOG = logging.getLogger(__name__) LOG = logging.getLogger(__name__)
class TestAodhAlarmTransformer(base.BaseTest): class TestAodhAlarmTransformer(AodhTransformerBaseTest):
OPTS = [ OPTS = [
cfg.StrOpt('update_method', cfg.StrOpt('update_method',
default=UpdateMethod.PUSH), default=UpdateMethod.PULL),
] ]
@classmethod @classmethod
@ -112,86 +105,49 @@ class TestAodhAlarmTransformer(base.BaseTest):
self._validate_action(event, wrapper) self._validate_action(event, wrapper)
def _validate_aodh_vertex_props(self, vertex, event):
self.assertEqual(EntityCategory.ALARM, vertex[VProps.CATEGORY]) class TestAodhAlarmPushTransformer(AodhTransformerBaseTest):
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] OPTS = [
if event_status == AodhState.OK: cfg.StrOpt('update_method',
self.assertEqual(AlarmProps.INACTIVE_STATE, default=UpdateMethod.PUSH),
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): @classmethod
if DSProps.EVENT_TYPE in alarm \ def setUpClass(cls):
and alarm[DSProps.EVENT_TYPE] in GraphAction.__dict__.values(): cls.transformers = {}
self.assertEqual(alarm[DSProps.EVENT_TYPE], wrapper.action) cls.conf = cfg.ConfigOpts()
return cls.conf.register_opts(cls.OPTS, group=AODH_DATASOURCE)
cls.transformers[AODH_DATASOURCE] = \
AodhTransformer(cls.transformers, cls.conf)
ds_action = alarm[DSProps.DATASOURCE_ACTION] def test_update_transform(self):
if ds_action in (DatasourceAction.SNAPSHOT, DatasourceAction.UPDATE): LOG.debug('Aodh update alarm transformer test:'
self.assertEqual(GraphAction.UPDATE_ENTITY, wrapper.action) 'transform entity event update')
else:
self.assertEqual(GraphAction.CREATE_ENTITY, wrapper.action)
def _validate_neighbors(self, neighbors, alarm_id, event): # Test setup
resource_counter = 0 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 event in static_events:
# convert neighbor from dict to vertex object
neighbors = event[TransformerBase.QUERY_RESULT]
vertices = []
for neighbor in neighbors: for neighbor in neighbors:
resource_id = event[AodhProps.RESOURCE_ID] vertices.append(self._convert_dist_to_vertex(neighbor))
self._validate_instance_neighbor(neighbor, event[TransformerBase.QUERY_RESULT] = vertices
resource_id,
alarm_id)
resource_counter += 1
self.assertEqual(1, # Test action
resource_counter, wrapper = self.transformers[AODH_DATASOURCE].transform(event)
'Alarm can be belonged to only one resource')
def _validate_instance_neighbor(self, # Test assertions
alarm_neighbor, vertex = wrapper.vertex
resource_id, self._validate_aodh_vertex_props(vertex, event)
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 neighbors = wrapper.neighbors
edge = alarm_neighbor.edge self.assertEqual(1, len(neighbors))
self.assertEqual(edge.target_id, alarm_neighbor.vertex.vertex_id) self._validate_neighbors(neighbors, vertex.vertex_id, event)
self.assertEqual(edge.source_id, alarm_vertex_id)
self.assertEqual(edge.label, EdgeLabel.ON)
def _convert_dist_to_vertex(self, neighbor): self._validate_action(event, wrapper)
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)

View File

@ -144,9 +144,6 @@ class NagiosDriverTest(NagiosBaseTest):
self.assertEqual(2, len(services)) self.assertEqual(2, len(services))
self._assert_contains(service_data1, services) self._assert_contains(service_data1, services)
self._assert_contains(service_data2, services) self._assert_contains(service_data2, services)
for service in services:
self.assertEqual(GraphAction.DELETE_ENTITY,
service[DSProps.EVENT_TYPE])
# Action # Action
services = nagios_driver._get_all_alarms() services = nagios_driver._get_all_alarms()
@ -278,9 +275,6 @@ class NagiosDriverTest(NagiosBaseTest):
self.assertEqual(2, len(services)) self.assertEqual(2, len(services))
self._assert_contains(service_data1, services) self._assert_contains(service_data1, services)
self._assert_contains(service_data2, services) self._assert_contains(service_data2, services)
for service in services:
self.assertEqual(GraphAction.DELETE_ENTITY,
service[DSProps.EVENT_TYPE])
# Action # Action
services = nagios_driver._get_changed_alarms() services = nagios_driver._get_changed_alarms()

View File

@ -90,6 +90,7 @@ class NagiosTransformerTest(base.BaseTest):
# Test action # Test action
wrapper = NagiosTransformer(self.transformers, self.conf).\ wrapper = NagiosTransformer(self.transformers, self.conf).\
transform(alarm) transform(alarm)
self._validate_vertex(wrapper.vertex, alarm) self._validate_vertex(wrapper.vertex, alarm)
neighbors = wrapper.neighbors neighbors = wrapper.neighbors
@ -103,13 +104,11 @@ class NagiosTransformerTest(base.BaseTest):
self._validate_action(alarm, wrapper) self._validate_action(alarm, wrapper)
def _validate_action(self, 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] ds_action = alarm[DSProps.DATASOURCE_ACTION]
if ds_action in (DatasourceAction.SNAPSHOT, DatasourceAction.UPDATE): if ds_action in (DatasourceAction.SNAPSHOT, DatasourceAction.UPDATE):
if alarm[NagiosProperties.STATUS] == 'OK':
self.assertEqual(GraphAction.DELETE_ENTITY, wrapper.action)
else:
self.assertEqual(GraphAction.UPDATE_ENTITY, wrapper.action) self.assertEqual(GraphAction.UPDATE_ENTITY, wrapper.action)
else: else:
self.assertEqual(GraphAction.CREATE_ENTITY, wrapper.action) self.assertEqual(GraphAction.CREATE_ENTITY, wrapper.action)

View File

@ -143,9 +143,6 @@ class ZabbixDriverTest(ZabbixBaseTest):
self.assertEqual(2, len(alarms)) self.assertEqual(2, len(alarms))
self._assert_contains(expected_alarm1, alarms) self._assert_contains(expected_alarm1, alarms)
self._assert_contains(expected_alarm2, 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 # Step 4 - get all when all alarms are inactivated and their status
# was not changed # was not changed
@ -254,9 +251,6 @@ class ZabbixDriverTest(ZabbixBaseTest):
self.assertEqual(2, len(alarms)) self.assertEqual(2, len(alarms))
self._assert_contains(expected_alarm1, alarms) self._assert_contains(expected_alarm1, alarms)
self._assert_contains(expected_alarm2, 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 # Step 6 - get changes when no change occurred
# Action # Action

View File

@ -108,13 +108,11 @@ class ZabbixTransformerTest(base.BaseTest):
self._validate_action(alarm, wrapper) self._validate_action(alarm, wrapper)
def _validate_action(self, 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] ds_action = alarm[DSProps.DATASOURCE_ACTION]
if ds_action in (DatasourceAction.SNAPSHOT, DatasourceAction.UPDATE): if ds_action in (DatasourceAction.SNAPSHOT, DatasourceAction.UPDATE):
if alarm[ZabbixProps.VALUE] == ZabbixTriggerValue.OK:
self.assertEqual(GraphAction.DELETE_ENTITY, wrapper.action)
else:
self.assertEqual(GraphAction.UPDATE_ENTITY, wrapper.action) self.assertEqual(GraphAction.UPDATE_ENTITY, wrapper.action)
else: else:
self.assertEqual(GraphAction.CREATE_ENTITY, wrapper.action) self.assertEqual(GraphAction.CREATE_ENTITY, wrapper.action)