Rewrite Aodh datasource
Implements: blueprint rewrite-aodh-datasource Depends-On: I4b1140c20c68821eaf0849c9ee551ff9b1c27deb Change-Id: Iaaa72dd5214f6d8168f4ba4560a6cec67d01d2a9 Signed-off-by: dongwenjuan <dong.wenjuan@zte.com.cn>
This commit is contained in:
parent
4e44ea6eeb
commit
501c0644d3
@ -6,6 +6,7 @@ pbr!=2.1.0,>=2.0.0 # Apache-2.0
|
|||||||
Babel!=2.4.0,>=2.3.4 # BSD
|
Babel!=2.4.0,>=2.3.4 # BSD
|
||||||
lxml!=3.7.0,>=3.4.1 # BSD
|
lxml!=3.7.0,>=3.4.1 # BSD
|
||||||
PyMySQL>=0.7.6 # MIT License
|
PyMySQL>=0.7.6 # MIT License
|
||||||
|
aodhclient>=0.9.0 # Apache-2.0
|
||||||
python-ceilometerclient>=2.5.0 # Apache-2.0
|
python-ceilometerclient>=2.5.0 # Apache-2.0
|
||||||
python-cinderclient>=3.2.0 # Apache-2.0
|
python-cinderclient>=3.2.0 # Apache-2.0
|
||||||
python-dateutil>=2.4.2 # BSD
|
python-dateutil>=2.4.2 # BSD
|
||||||
|
@ -19,6 +19,7 @@ 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 AodhEventType
|
||||||
|
from vitrage.datasources.aodh.properties import AodhExtendedAlarmType
|
||||||
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
|
||||||
@ -34,12 +35,44 @@ class AodhDriver(AlarmDriverBase):
|
|||||||
self._client = None
|
self._client = None
|
||||||
self.conf = conf
|
self.conf = conf
|
||||||
self._init_aodh_event_actions()
|
self._init_aodh_event_actions()
|
||||||
|
self._init_convert_aodh_alarm_rule_actions()
|
||||||
|
self._init_alarm_type_to_rule()
|
||||||
self._cache_all_alarms()
|
self._cache_all_alarms()
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
def _init_convert_aodh_alarm_rule_actions(self):
|
||||||
|
self.convert_rule_actions = {
|
||||||
|
AodhExtendedAlarmType.VITRAGE:
|
||||||
|
self._convert_vitrage_alarm_rule,
|
||||||
|
AodhExtendedAlarmType.EVENT:
|
||||||
|
self._convert_event_alarm_rule,
|
||||||
|
AodhExtendedAlarmType.THRESHOLD:
|
||||||
|
self._convert_threshold_alarm_rule,
|
||||||
|
AodhExtendedAlarmType.GNOCCHI_RESOURCES_THRESHOLD:
|
||||||
|
self._convert_gnocchi_resources_threshold_alarm_rule
|
||||||
|
}
|
||||||
|
|
||||||
|
def _init_alarm_type_to_rule(self):
|
||||||
|
self.alarm_rule_types = {
|
||||||
|
AodhExtendedAlarmType.VITRAGE: AodhProps.EVENT_RULE,
|
||||||
|
AodhExtendedAlarmType.EVENT: AodhProps.EVENT_RULE,
|
||||||
|
AodhExtendedAlarmType.THRESHOLD: AodhProps.THRESHOLD_RULE,
|
||||||
|
AodhExtendedAlarmType.GNOCCHI_RESOURCES_THRESHOLD:
|
||||||
|
AodhProps.GNOCCHI_RESOURCES_THRESHOLD_RULE
|
||||||
|
}
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def client(self):
|
def client(self):
|
||||||
if not self._client:
|
if not self._client:
|
||||||
self._client = os_clients.ceilometer_client(self.conf)
|
self._client = os_clients.aodh_client(self.conf)
|
||||||
return self._client
|
return self._client
|
||||||
|
|
||||||
def _vitrage_type(self):
|
def _vitrage_type(self):
|
||||||
@ -55,7 +88,7 @@ class AodhDriver(AlarmDriverBase):
|
|||||||
|
|
||||||
def _get_alarms(self):
|
def _get_alarms(self):
|
||||||
try:
|
try:
|
||||||
aodh_alarms = self.client.alarms.list()
|
aodh_alarms = self.client.alarm.list()
|
||||||
return [self._convert_alarm(alarm) for alarm in
|
return [self._convert_alarm(alarm) for alarm in
|
||||||
aodh_alarms if alarm is not None]
|
aodh_alarms if alarm is not None]
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
@ -72,123 +105,37 @@ class AodhDriver(AlarmDriverBase):
|
|||||||
def _is_valid(self, alarm):
|
def _is_valid(self, alarm):
|
||||||
return True
|
return True
|
||||||
|
|
||||||
@classmethod
|
def _get_aodh_alarm_type(self, alarm):
|
||||||
def _convert_event_alarm(cls, alarm):
|
|
||||||
res = cls._convert_base_alarm(alarm)
|
|
||||||
res[AodhProps.EVENT_TYPE] = alarm.event_rule[AodhProps.EVENT_TYPE],
|
|
||||||
res[AodhProps.RESOURCE_ID] = _parse_query(alarm.event_rule,
|
|
||||||
AodhProps.EVENT_RESOURCE_ID)
|
|
||||||
return res
|
|
||||||
|
|
||||||
@classmethod
|
Aodh_type = [AodhExtendedAlarmType.EVENT,
|
||||||
def _convert_threshold_alarm(cls, alarm):
|
AodhExtendedAlarmType.THRESHOLD,
|
||||||
res = cls._convert_base_alarm(alarm)
|
AodhExtendedAlarmType.GNOCCHI_RESOURCES_THRESHOLD]
|
||||||
res[AodhProps.STATE_TIMESTAMP] = alarm.state_timestamp
|
|
||||||
res[AodhProps.RESOURCE_ID] = _parse_query(alarm.threshold_rule,
|
|
||||||
AodhProps.RESOURCE_ID)
|
|
||||||
return res
|
|
||||||
|
|
||||||
@classmethod
|
alarm_type = alarm[AodhProps.TYPE]
|
||||||
def _convert_gnocchi_resources_threshold(cls, alarm):
|
|
||||||
res = cls._convert_base_alarm_gnocchi(alarm)
|
|
||||||
if type(alarm) is not dict:
|
|
||||||
alarm = alarm.to_dict()
|
|
||||||
res[AodhProps.STATE_TIMESTAMP] = \
|
|
||||||
alarm.get(AodhProps.STATE_TIMESTAMP)
|
|
||||||
res[AodhProps.RESOURCE_ID] = \
|
|
||||||
alarm.get(AodhProps.GNOCCHI_RESOURCES_THRESHOLD_RULE,
|
|
||||||
{}).get(AodhProps.RESOURCE_ID)
|
|
||||||
else:
|
|
||||||
res[AodhProps.STATE_TIMESTAMP] = \
|
|
||||||
alarm.get(AodhProps.DETAIL, {}).get(AodhProps.STATE_TIMESTAMP)
|
|
||||||
res[AodhProps.RESOURCE_ID] = \
|
|
||||||
alarm.get(AodhProps.DETAIL,
|
|
||||||
{}).get(AodhProps.RULE,
|
|
||||||
{}).get(AodhProps.RESOURCE_ID)
|
|
||||||
return res
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def _convert_vitrage_alarm(cls, alarm):
|
|
||||||
res = cls._convert_base_alarm(alarm)
|
|
||||||
res[AodhProps.VITRAGE_ID] = _parse_query(alarm.event_rule,
|
|
||||||
AodhProps.VITRAGE_ID)
|
|
||||||
res[AodhProps.RESOURCE_ID] = _parse_query(alarm.event_rule,
|
|
||||||
AodhProps.RESOURCE_ID)
|
|
||||||
return res
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def _convert_base_dict_alarm_gnocchi(alarm):
|
|
||||||
detail = alarm.get(AodhProps.DETAIL)
|
|
||||||
return {
|
|
||||||
AodhProps.SEVERITY: alarm.get(AodhProps.SEVERITY),
|
|
||||||
AodhProps.PROJECT_ID: alarm.get(AodhProps.PROJECT_ID),
|
|
||||||
AodhProps.TIMESTAMP: alarm.get(AodhProps.TIMESTAMP),
|
|
||||||
AodhProps.TYPE: alarm.get(AodhProps.TYPE),
|
|
||||||
AodhProps.ALARM_ID: alarm.get(AodhProps.ALARM_ID),
|
|
||||||
AodhProps.DESCRIPTION: detail.get(AodhProps.DESCRIPTION),
|
|
||||||
AodhProps.ENABLED: detail.get(AodhProps.ENABLED),
|
|
||||||
AodhProps.NAME: detail.get(AodhProps.NAME),
|
|
||||||
AodhProps.REPEAT_ACTIONS: detail.get(AodhProps.REPEAT_ACTIONS),
|
|
||||||
AodhProps.STATE: detail.get(AodhProps.STATE)
|
|
||||||
}
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def _convert_base_non_dict_alarm_gnocchi(alarm):
|
|
||||||
alarm = alarm.to_dict()
|
|
||||||
return {
|
|
||||||
AodhProps.SEVERITY: alarm.get(AodhProps.SEVERITY),
|
|
||||||
AodhProps.DESCRIPTION: alarm.get(AodhProps.DESCRIPTION),
|
|
||||||
AodhProps.ENABLED: alarm.get(AodhProps.ENABLED),
|
|
||||||
AodhProps.ALARM_ID: alarm.get(AodhProps.ALARM_ID),
|
|
||||||
AodhProps.NAME: alarm.get(AodhProps.NAME),
|
|
||||||
AodhProps.PROJECT_ID: alarm.get(AodhProps.PROJECT_ID),
|
|
||||||
AodhProps.REPEAT_ACTIONS: alarm.get(AodhProps.REPEAT_ACTIONS),
|
|
||||||
AodhProps.STATE: alarm.get(AodhProps.STATE),
|
|
||||||
AodhProps.TIMESTAMP: alarm.get(AodhProps.TIMESTAMP),
|
|
||||||
AodhProps.TYPE: alarm.get(AodhProps.TYPE)
|
|
||||||
}
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def _convert_base_alarm_gnocchi(cls, alarm):
|
|
||||||
"""distinguish between alarm received by notification (type dict)
|
|
||||||
|
|
||||||
to alarm received by _get_alarms() (type alarm).
|
|
||||||
"""
|
|
||||||
|
|
||||||
if type(alarm) is dict:
|
|
||||||
return cls._convert_base_dict_alarm_gnocchi(alarm)
|
|
||||||
|
|
||||||
return cls._convert_base_non_dict_alarm_gnocchi(alarm)
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def _convert_base_alarm(alarm):
|
|
||||||
return {
|
|
||||||
AodhProps.SEVERITY: alarm.severity,
|
|
||||||
AodhProps.DESCRIPTION: alarm.description,
|
|
||||||
AodhProps.ENABLED: alarm.enabled,
|
|
||||||
AodhProps.ALARM_ID: alarm.alarm_id,
|
|
||||||
AodhProps.NAME: alarm.name,
|
|
||||||
AodhProps.PROJECT_ID: alarm.project_id,
|
|
||||||
AodhProps.REPEAT_ACTIONS: alarm.repeat_actions,
|
|
||||||
AodhProps.STATE: alarm.state,
|
|
||||||
AodhProps.TIMESTAMP: alarm.timestamp,
|
|
||||||
AodhProps.TYPE: alarm.type
|
|
||||||
}
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def _convert_alarm(cls, alarm):
|
|
||||||
alarm_type = alarm.type
|
|
||||||
if alarm_type == AodhProps.EVENT and \
|
if alarm_type == AodhProps.EVENT and \
|
||||||
_is_vitrage_alarm(alarm.event_rule):
|
_is_vitrage_alarm(alarm.get(AodhProps.EVENT_RULE)
|
||||||
return cls._convert_vitrage_alarm(alarm)
|
or alarm.get(AodhProps.RULE)):
|
||||||
elif alarm_type == AodhProps.EVENT:
|
return AodhExtendedAlarmType.VITRAGE
|
||||||
return cls._convert_event_alarm(alarm)
|
elif alarm_type not in Aodh_type:
|
||||||
elif alarm_type == AodhProps.THRESHOLD:
|
|
||||||
return cls._convert_threshold_alarm(alarm)
|
|
||||||
elif alarm_type == AodhProps.GNOCCHI_RESOURCES_THRESHOLD:
|
|
||||||
return cls._convert_gnocchi_resources_threshold(alarm)
|
|
||||||
else:
|
|
||||||
LOG.warning('Unsupported Aodh alarm of type %s' % alarm_type)
|
LOG.warning('Unsupported Aodh alarm of type %s' % alarm_type)
|
||||||
|
alarm_type = None
|
||||||
|
|
||||||
|
return alarm_type
|
||||||
|
|
||||||
|
def _convert_alarm(self, alarm):
|
||||||
|
|
||||||
|
entity = self._convert_alarm_common(alarm)
|
||||||
|
|
||||||
|
detail = self._convert_alarm_detail(alarm)
|
||||||
|
entity.update(detail)
|
||||||
|
|
||||||
|
alarm_type = self._get_aodh_alarm_type(alarm)
|
||||||
|
alarm_rule_type = self.alarm_rule_types[alarm_type]
|
||||||
|
alarm_rule = alarm[alarm_rule_type]
|
||||||
|
rule = self._convert_alarm_rule(alarm_type, alarm_rule)
|
||||||
|
entity.update(rule)
|
||||||
|
|
||||||
|
return entity
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_event_types():
|
def get_event_types():
|
||||||
@ -215,52 +162,27 @@ class AodhDriver(AlarmDriverBase):
|
|||||||
AODH_DATASOURCE,
|
AODH_DATASOURCE,
|
||||||
DatasourceAction.UPDATE)[0]
|
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
|
@classmethod
|
||||||
def _convert_base_event(cls, event):
|
def _convert_vitrage_alarm_rule(cls, rule):
|
||||||
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 {
|
return {
|
||||||
AodhProps.VITRAGE_ID: _parse_query(rule, AodhProps.VITRAGE_ID),
|
AodhProps.VITRAGE_ID: _parse_query(rule, AodhProps.VITRAGE_ID),
|
||||||
AodhProps.RESOURCE_ID: _parse_query(rule, AodhProps.RESOURCE_ID)
|
AodhProps.RESOURCE_ID: _parse_query(rule, AodhProps.RESOURCE_ID)
|
||||||
}
|
}
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def _convert_threshold_alarm_event(cls, event):
|
def _convert_threshold_alarm_rule(cls, rule):
|
||||||
rule = event[AodhProps.DETAIL][AodhProps.RULE]
|
|
||||||
return {
|
return {
|
||||||
AodhProps.RESOURCE_ID: _parse_query(rule, AodhProps.RESOURCE_ID),
|
AodhProps.RESOURCE_ID: _parse_query(rule, AodhProps.RESOURCE_ID)
|
||||||
AodhProps.STATE_TIMESTAMP: event[AodhProps.STATE_TIMESTAMP]
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def _convert_gnocchi_resources_threshold_alarm_event(cls, event):
|
def _convert_gnocchi_resources_threshold_alarm_rule(cls, rule):
|
||||||
return {
|
return {
|
||||||
AodhProps.RESOURCE_ID: event.get(
|
AodhProps.RESOURCE_ID: _parse_query(rule, AodhProps.RESOURCE_ID)
|
||||||
AodhProps.DETAIL, {}).get(AodhProps.RULE,
|
|
||||||
{}).get(AodhProps.RESOURCE_ID),
|
|
||||||
AodhProps.STATE_TIMESTAMP:
|
|
||||||
event.get(AodhProps.DETAIL, {}).get(AodhProps.STATE_TIMESTAMP)
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def _convert_event_alarm_event(cls, rule):
|
def _convert_event_alarm_rule(cls, rule):
|
||||||
return {
|
return {
|
||||||
AodhProps.EVENT_TYPE: rule[AodhProps.EVENT_TYPE],
|
AodhProps.EVENT_TYPE: rule[AodhProps.EVENT_TYPE],
|
||||||
AodhProps.RESOURCE_ID:
|
AodhProps.RESOURCE_ID:
|
||||||
@ -268,32 +190,37 @@ class AodhDriver(AlarmDriverBase):
|
|||||||
}
|
}
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def _convert_detail_event(cls, event):
|
def _convert_alarm_common(cls, alarm):
|
||||||
alarm_info = event[AodhProps.DETAIL]
|
return {
|
||||||
alarm_rule = alarm_info[AodhProps.RULE]
|
AodhProps.ALARM_ID: alarm.get(AodhProps.ALARM_ID),
|
||||||
|
AodhProps.USER_ID: alarm.get(AodhProps.USER_ID),
|
||||||
entity_detail = {
|
AodhProps.PROJECT_ID: alarm.get(AodhProps.PROJECT_ID),
|
||||||
AodhProps.DESCRIPTION: alarm_info[AodhProps.DESCRIPTION],
|
AodhProps.SEVERITY: alarm.get(AodhProps.SEVERITY),
|
||||||
AodhProps.ENABLED: alarm_info[AodhProps.ENABLED],
|
AodhProps.TIMESTAMP: alarm.get(AodhProps.TIMESTAMP)
|
||||||
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):
|
@classmethod
|
||||||
entity_detail.update(cls._convert_vitrage_alarm_event(alarm_rule))
|
def _convert_alarm_detail(cls, alarm):
|
||||||
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))
|
|
||||||
elif entity_detail[AodhProps.TYPE] == \
|
|
||||||
AodhProps.GNOCCHI_RESOURCES_THRESHOLD:
|
|
||||||
entity_detail.update(
|
|
||||||
cls._convert_gnocchi_resources_threshold(event))
|
|
||||||
|
|
||||||
return entity_detail
|
return {
|
||||||
|
AodhProps.DESCRIPTION: alarm.get(AodhProps.DESCRIPTION),
|
||||||
|
AodhProps.ENABLED: alarm.get(AodhProps.ENABLED),
|
||||||
|
AodhProps.NAME: alarm.get(AodhProps.NAME),
|
||||||
|
AodhProps.STATE: alarm.get(AodhProps.STATE),
|
||||||
|
AodhProps.REPEAT_ACTIONS: alarm.get(AodhProps.REPEAT_ACTIONS),
|
||||||
|
AodhProps.TYPE: alarm.get(AodhProps.TYPE),
|
||||||
|
AodhProps.STATE_TIMESTAMP: alarm.get(AodhProps.STATE_TIMESTAMP),
|
||||||
|
AodhProps.STATE_REASON: alarm.get(AodhProps.STATE_REASON)
|
||||||
|
}
|
||||||
|
|
||||||
|
def _convert_alarm_rule(self, alarm_type, alarm_rule):
|
||||||
|
if alarm_type in self.convert_rule_actions:
|
||||||
|
entity = self.convert_rule_actions[alarm_type](alarm_rule)
|
||||||
|
else:
|
||||||
|
LOG.warning('Unsupported Aodh alarm type %s' % alarm_type)
|
||||||
|
return None
|
||||||
|
|
||||||
|
return entity
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def _parse_changed_rule(cls, change_rule):
|
def _parse_changed_rule(cls, change_rule):
|
||||||
@ -312,10 +239,17 @@ class AodhDriver(AlarmDriverBase):
|
|||||||
return entity
|
return entity
|
||||||
|
|
||||||
def _convert_alarm_creation_event(self, event):
|
def _convert_alarm_creation_event(self, event):
|
||||||
entity = self._convert_base_event(event)
|
entity = self._convert_alarm_common(event)
|
||||||
detail = self._convert_detail_event(event)
|
|
||||||
|
alarm_info = event[AodhProps.DETAIL]
|
||||||
|
detail = self._convert_alarm_detail(alarm_info)
|
||||||
entity.update(detail)
|
entity.update(detail)
|
||||||
|
|
||||||
|
alarm_type = self._get_aodh_alarm_type(alarm_info)
|
||||||
|
alarm_rule = alarm_info[AodhProps.RULE]
|
||||||
|
rule_info = self._convert_alarm_rule(alarm_type, alarm_rule)
|
||||||
|
entity.update(rule_info)
|
||||||
|
|
||||||
return self._filter_and_cache_alarm(entity, None,
|
return self._filter_and_cache_alarm(entity, None,
|
||||||
self._filter_get_erroneous,
|
self._filter_get_erroneous,
|
||||||
datetime_utils.utcnow(False))
|
datetime_utils.utcnow(False))
|
||||||
@ -358,14 +292,6 @@ class AodhDriver(AlarmDriverBase):
|
|||||||
except Exception as e:
|
except Exception as e:
|
||||||
LOG.exception("Failed to Convert alarm state"
|
LOG.exception("Failed to Convert alarm state"
|
||||||
" transition event - %s", e)
|
" transition event - %s", e)
|
||||||
try:
|
|
||||||
state = event[AodhProps.DETAIL] # type unicode
|
|
||||||
unicode_to_str = state.encode("ascii")
|
|
||||||
str_to_dict = eval(unicode_to_str)
|
|
||||||
entity[AodhProps.STATE] = str_to_dict[AodhProps.STATE]
|
|
||||||
except Exception as e:
|
|
||||||
LOG.exception("Failed to Convert alarm state "
|
|
||||||
"transition event - %s", e)
|
|
||||||
|
|
||||||
return self._filter_and_cache_alarm(entity, old_alarm,
|
return self._filter_and_cache_alarm(entity, old_alarm,
|
||||||
self._filter_get_change,
|
self._filter_get_change,
|
||||||
|
@ -18,6 +18,7 @@ class AodhProperties(object):
|
|||||||
DESCRIPTION = 'description'
|
DESCRIPTION = 'description'
|
||||||
ENABLED = 'enabled'
|
ENABLED = 'enabled'
|
||||||
EVENT = 'event'
|
EVENT = 'event'
|
||||||
|
EVENT_RULE = 'event_rule'
|
||||||
EVENT_TYPE = 'event_type'
|
EVENT_TYPE = 'event_type'
|
||||||
EVENT_RESOURCE_ID = 'traits.resource_id'
|
EVENT_RESOURCE_ID = 'traits.resource_id'
|
||||||
NAME = 'name'
|
NAME = 'name'
|
||||||
@ -29,6 +30,7 @@ class AodhProperties(object):
|
|||||||
SEVERITY = 'severity'
|
SEVERITY = 'severity'
|
||||||
STATE_TIMESTAMP = 'state_timestamp'
|
STATE_TIMESTAMP = 'state_timestamp'
|
||||||
THRESHOLD = 'threshold'
|
THRESHOLD = 'threshold'
|
||||||
|
THRESHOLD_RULE = 'threshold_rule'
|
||||||
GNOCCHI_RESOURCES_THRESHOLD = 'gnocchi_resources_threshold'
|
GNOCCHI_RESOURCES_THRESHOLD = 'gnocchi_resources_threshold'
|
||||||
TIMESTAMP = 'timestamp'
|
TIMESTAMP = 'timestamp'
|
||||||
TYPE = 'type'
|
TYPE = 'type'
|
||||||
@ -36,6 +38,8 @@ class AodhProperties(object):
|
|||||||
DETAIL = 'detail'
|
DETAIL = 'detail'
|
||||||
RULE = 'rule'
|
RULE = 'rule'
|
||||||
GNOCCHI_RESOURCES_THRESHOLD_RULE = 'gnocchi_resources_threshold_rule'
|
GNOCCHI_RESOURCES_THRESHOLD_RULE = 'gnocchi_resources_threshold_rule'
|
||||||
|
USER_ID = 'user_id'
|
||||||
|
STATE_REASON = 'state_reason'
|
||||||
|
|
||||||
|
|
||||||
class AodhState(object):
|
class AodhState(object):
|
||||||
@ -49,3 +53,10 @@ class AodhEventType(object):
|
|||||||
RULE_CHANGE = 'alarm.rule_change'
|
RULE_CHANGE = 'alarm.rule_change'
|
||||||
STATE_TRANSITION = 'alarm.state_transition'
|
STATE_TRANSITION = 'alarm.state_transition'
|
||||||
DELETION = 'alarm.deletion'
|
DELETION = 'alarm.deletion'
|
||||||
|
|
||||||
|
|
||||||
|
class AodhExtendedAlarmType(object):
|
||||||
|
EVENT = 'event'
|
||||||
|
VITRAGE = 'vitrage'
|
||||||
|
THRESHOLD = 'threshold'
|
||||||
|
GNOCCHI_RESOURCES_THRESHOLD = 'gnocchi_resources_threshold'
|
||||||
|
44
vitrage/datasources/ceilometer/__init__.py
Normal file
44
vitrage/datasources/ceilometer/__init__.py
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
# Copyright 2016 - Nokia
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||||
|
# not use this file except in compliance with the License. You may obtain
|
||||||
|
# a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
# License for the specific language governing permissions and limitations
|
||||||
|
# under the License.
|
||||||
|
|
||||||
|
from oslo_config import cfg
|
||||||
|
from vitrage.common.constants import DatasourceOpts as DSOpts
|
||||||
|
from vitrage.common.constants import UpdateMethod
|
||||||
|
|
||||||
|
CEILOMETER_DATASOURCE = 'ceilometer'
|
||||||
|
|
||||||
|
OPTS = [
|
||||||
|
cfg.StrOpt(DSOpts.TRANSFORMER,
|
||||||
|
default='vitrage.datasources.ceilometer.'
|
||||||
|
'transformer.CeilometerTransformer',
|
||||||
|
help='Ceilometer transformer class path',
|
||||||
|
required=True),
|
||||||
|
cfg.StrOpt(DSOpts.DRIVER,
|
||||||
|
default='vitrage.datasources.ceilometer.'
|
||||||
|
'driver.CeilometerDriver',
|
||||||
|
help='ceilometer driver class path',
|
||||||
|
required=True),
|
||||||
|
cfg.StrOpt(DSOpts.UPDATE_METHOD,
|
||||||
|
default=UpdateMethod.PUSH,
|
||||||
|
help='None: updates only via Vitrage periodic snapshots.'
|
||||||
|
'Pull: updates every [changes_interval] seconds.'
|
||||||
|
'Push: updates by getting notifications from the'
|
||||||
|
' datasource itself.',
|
||||||
|
required=True),
|
||||||
|
cfg.IntOpt(DSOpts.CHANGES_INTERVAL,
|
||||||
|
default=10,
|
||||||
|
min=10,
|
||||||
|
help='interval between checking changes'
|
||||||
|
'in ceilometer data source'),
|
||||||
|
]
|
385
vitrage/datasources/ceilometer/driver.py
Normal file
385
vitrage/datasources/ceilometer/driver.py
Normal file
@ -0,0 +1,385 @@
|
|||||||
|
# Copyright 2016 - Alcatel-Lucent
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||||
|
# not use this file except in compliance with the License. You may obtain
|
||||||
|
# a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
# License for the specific language governing permissions and limitations
|
||||||
|
# under the License.
|
||||||
|
|
||||||
|
from oslo_log import log
|
||||||
|
|
||||||
|
from vitrage.common.constants import DatasourceAction
|
||||||
|
from vitrage.common.constants import DatasourceProperties as DSProps
|
||||||
|
from vitrage.datasources.alarm_driver_base import AlarmDriverBase
|
||||||
|
from vitrage.datasources.ceilometer import CEILOMETER_DATASOURCE
|
||||||
|
from vitrage.datasources.ceilometer.properties \
|
||||||
|
import CeilometerEventType as CeilEventType
|
||||||
|
from vitrage.datasources.ceilometer.properties \
|
||||||
|
import CeilometerProperties as CeilProps
|
||||||
|
from vitrage.datasources.ceilometer.properties \
|
||||||
|
import CeilometerState as CeilState
|
||||||
|
from vitrage import os_clients
|
||||||
|
from vitrage.utils import datetime as datetime_utils
|
||||||
|
|
||||||
|
LOG = log.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
class CeilometerDriver(AlarmDriverBase):
|
||||||
|
|
||||||
|
def __init__(self, conf):
|
||||||
|
super(CeilometerDriver, self).__init__()
|
||||||
|
self._client = None
|
||||||
|
self.conf = conf
|
||||||
|
self._init_aodh_event_actions()
|
||||||
|
self._cache_all_alarms()
|
||||||
|
|
||||||
|
@property
|
||||||
|
def client(self):
|
||||||
|
if not self._client:
|
||||||
|
self._client = os_clients.ceilometer_client(self.conf)
|
||||||
|
return self._client
|
||||||
|
|
||||||
|
def _vitrage_type(self):
|
||||||
|
return CEILOMETER_DATASOURCE
|
||||||
|
|
||||||
|
def _alarm_key(self, alarm):
|
||||||
|
return alarm[CeilProps.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 if alarm is not None]
|
||||||
|
except Exception as e:
|
||||||
|
LOG.exception("Failed to get all alarms, Exception: %s", e)
|
||||||
|
return []
|
||||||
|
|
||||||
|
def _is_erroneous(self, alarm):
|
||||||
|
return alarm and alarm[CeilProps.STATE] == CeilState.ALARM
|
||||||
|
|
||||||
|
def _status_changed(self, alarm1, alarm2):
|
||||||
|
return alarm1 and alarm2 and \
|
||||||
|
not alarm1[CeilProps.STATE] == alarm2[CeilProps.STATE]
|
||||||
|
|
||||||
|
def _is_valid(self, alarm):
|
||||||
|
return True
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def _convert_event_alarm(cls, alarm):
|
||||||
|
res = cls._convert_base_alarm(alarm)
|
||||||
|
res[CeilProps.EVENT_TYPE] = alarm.event_rule[CeilProps.EVENT_TYPE],
|
||||||
|
res[CeilProps.RESOURCE_ID] = _parse_query(alarm.event_rule,
|
||||||
|
CeilProps.EVENT_RESOURCE_ID)
|
||||||
|
return res
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def _convert_threshold_alarm(cls, alarm):
|
||||||
|
res = cls._convert_base_alarm(alarm)
|
||||||
|
res[CeilProps.STATE_TIMESTAMP] = alarm.state_timestamp
|
||||||
|
res[CeilProps.RESOURCE_ID] = _parse_query(alarm.threshold_rule,
|
||||||
|
CeilProps.RESOURCE_ID)
|
||||||
|
return res
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def _convert_gnocchi_resources_threshold(cls, alarm):
|
||||||
|
res = cls._convert_base_alarm_gnocchi(alarm)
|
||||||
|
if type(alarm) is not dict:
|
||||||
|
alarm = alarm.to_dict()
|
||||||
|
res[CeilProps.STATE_TIMESTAMP] = \
|
||||||
|
alarm.get(CeilProps.STATE_TIMESTAMP)
|
||||||
|
res[CeilProps.RESOURCE_ID] = \
|
||||||
|
alarm.get(CeilProps.GNOCCHI_RESOURCES_THRESHOLD_RULE,
|
||||||
|
{}).get(CeilProps.RESOURCE_ID)
|
||||||
|
else:
|
||||||
|
res[CeilProps.STATE_TIMESTAMP] = \
|
||||||
|
alarm.get(CeilProps.DETAIL, {}).get(CeilProps.STATE_TIMESTAMP)
|
||||||
|
res[CeilProps.RESOURCE_ID] = \
|
||||||
|
alarm.get(CeilProps.DETAIL,
|
||||||
|
{}).get(CeilProps.RULE,
|
||||||
|
{}).get(CeilProps.RESOURCE_ID)
|
||||||
|
return res
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def _convert_vitrage_alarm(cls, alarm):
|
||||||
|
res = cls._convert_base_alarm(alarm)
|
||||||
|
res[CeilProps.VITRAGE_ID] = _parse_query(alarm.event_rule,
|
||||||
|
CeilProps.VITRAGE_ID)
|
||||||
|
res[CeilProps.RESOURCE_ID] = _parse_query(alarm.event_rule,
|
||||||
|
CeilProps.RESOURCE_ID)
|
||||||
|
return res
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _convert_base_dict_alarm_gnocchi(alarm):
|
||||||
|
detail = alarm.get(CeilProps.DETAIL)
|
||||||
|
return {
|
||||||
|
CeilProps.SEVERITY: alarm.get(CeilProps.SEVERITY),
|
||||||
|
CeilProps.PROJECT_ID: alarm.get(CeilProps.PROJECT_ID),
|
||||||
|
CeilProps.TIMESTAMP: alarm.get(CeilProps.TIMESTAMP),
|
||||||
|
CeilProps.TYPE: alarm.get(CeilProps.TYPE),
|
||||||
|
CeilProps.ALARM_ID: alarm.get(CeilProps.ALARM_ID),
|
||||||
|
CeilProps.DESCRIPTION: detail.get(CeilProps.DESCRIPTION),
|
||||||
|
CeilProps.ENABLED: detail.get(CeilProps.ENABLED),
|
||||||
|
CeilProps.NAME: detail.get(CeilProps.NAME),
|
||||||
|
CeilProps.REPEAT_ACTIONS: detail.get(CeilProps.REPEAT_ACTIONS),
|
||||||
|
CeilProps.STATE: detail.get(CeilProps.STATE)
|
||||||
|
}
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _convert_base_non_dict_alarm_gnocchi(alarm):
|
||||||
|
alarm = alarm.to_dict()
|
||||||
|
return {
|
||||||
|
CeilProps.SEVERITY: alarm.get(CeilProps.SEVERITY),
|
||||||
|
CeilProps.DESCRIPTION: alarm.get(CeilProps.DESCRIPTION),
|
||||||
|
CeilProps.ENABLED: alarm.get(CeilProps.ENABLED),
|
||||||
|
CeilProps.ALARM_ID: alarm.get(CeilProps.ALARM_ID),
|
||||||
|
CeilProps.NAME: alarm.get(CeilProps.NAME),
|
||||||
|
CeilProps.PROJECT_ID: alarm.get(CeilProps.PROJECT_ID),
|
||||||
|
CeilProps.REPEAT_ACTIONS: alarm.get(CeilProps.REPEAT_ACTIONS),
|
||||||
|
CeilProps.STATE: alarm.get(CeilProps.STATE),
|
||||||
|
CeilProps.TIMESTAMP: alarm.get(CeilProps.TIMESTAMP),
|
||||||
|
CeilProps.TYPE: alarm.get(CeilProps.TYPE)
|
||||||
|
}
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def _convert_base_alarm_gnocchi(cls, alarm):
|
||||||
|
"""distinguish between alarm received by notification (type dict)
|
||||||
|
|
||||||
|
to alarm received by _get_alarms() (type alarm).
|
||||||
|
"""
|
||||||
|
|
||||||
|
if type(alarm) is dict:
|
||||||
|
return cls._convert_base_dict_alarm_gnocchi(alarm)
|
||||||
|
|
||||||
|
return cls._convert_base_non_dict_alarm_gnocchi(alarm)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _convert_base_alarm(alarm):
|
||||||
|
return {
|
||||||
|
CeilProps.SEVERITY: alarm.severity,
|
||||||
|
CeilProps.DESCRIPTION: alarm.description,
|
||||||
|
CeilProps.ENABLED: alarm.enabled,
|
||||||
|
CeilProps.ALARM_ID: alarm.alarm_id,
|
||||||
|
CeilProps.NAME: alarm.name,
|
||||||
|
CeilProps.PROJECT_ID: alarm.project_id,
|
||||||
|
CeilProps.REPEAT_ACTIONS: alarm.repeat_actions,
|
||||||
|
CeilProps.STATE: alarm.state,
|
||||||
|
CeilProps.TIMESTAMP: alarm.timestamp,
|
||||||
|
CeilProps.TYPE: alarm.type
|
||||||
|
}
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def _convert_alarm(cls, alarm):
|
||||||
|
alarm_type = alarm.type
|
||||||
|
if alarm_type == CeilProps.EVENT and \
|
||||||
|
_is_vitrage_alarm(alarm.event_rule):
|
||||||
|
return cls._convert_vitrage_alarm(alarm)
|
||||||
|
elif alarm_type == CeilProps.EVENT:
|
||||||
|
return cls._convert_event_alarm(alarm)
|
||||||
|
elif alarm_type == CeilProps.THRESHOLD:
|
||||||
|
return cls._convert_threshold_alarm(alarm)
|
||||||
|
elif alarm_type == CeilProps.GNOCCHI_RESOURCES_THRESHOLD:
|
||||||
|
return cls._convert_gnocchi_resources_threshold(alarm)
|
||||||
|
else:
|
||||||
|
LOG.warning('Unsupported Ceilometer alarm type %s' % alarm_type)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def get_event_types():
|
||||||
|
# Add event_types to receive notifications about
|
||||||
|
return [CeilEventType.CREATION,
|
||||||
|
CeilEventType.STATE_TRANSITION,
|
||||||
|
CeilEventType.RULE_CHANGE,
|
||||||
|
CeilEventType.DELETION]
|
||||||
|
|
||||||
|
def enrich_event(self, event, event_type):
|
||||||
|
if event_type in self.actions:
|
||||||
|
entity = self.actions[event_type](event)
|
||||||
|
else:
|
||||||
|
LOG.warning('Unsupported Ceilometer 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 CeilometerDriver.make_pickleable(
|
||||||
|
[entity], CEILOMETER_DATASOURCE,
|
||||||
|
DatasourceAction.UPDATE)[0]
|
||||||
|
|
||||||
|
def _init_aodh_event_actions(self):
|
||||||
|
self.actions = {
|
||||||
|
CeilEventType.CREATION:
|
||||||
|
self._convert_alarm_creation_event,
|
||||||
|
CeilEventType.RULE_CHANGE:
|
||||||
|
self._convert_alarm_rule_change_event,
|
||||||
|
CeilEventType.STATE_TRANSITION:
|
||||||
|
self._convert_alarm_state_transition_event,
|
||||||
|
CeilEventType.DELETION:
|
||||||
|
self._convert_alarm_deletion_event
|
||||||
|
}
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def _convert_base_event(cls, event):
|
||||||
|
return {
|
||||||
|
CeilProps.PROJECT_ID: event[CeilProps.PROJECT_ID],
|
||||||
|
CeilProps.ALARM_ID: event[CeilProps.ALARM_ID],
|
||||||
|
CeilProps.SEVERITY: event[CeilProps.SEVERITY],
|
||||||
|
CeilProps.TIMESTAMP: event[CeilProps.TIMESTAMP],
|
||||||
|
CeilProps.USER_ID: event[CeilProps.USER_ID]
|
||||||
|
}
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def _convert_vitrage_alarm_rule(cls, rule):
|
||||||
|
return {
|
||||||
|
CeilProps.VITRAGE_ID: _parse_query(rule, CeilProps.VITRAGE_ID),
|
||||||
|
CeilProps.RESOURCE_ID: _parse_query(rule, CeilProps.RESOURCE_ID)
|
||||||
|
}
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def _convert_threshold_alarm_rule(cls, rule):
|
||||||
|
return {
|
||||||
|
CeilProps.RESOURCE_ID: _parse_query(rule, CeilProps.RESOURCE_ID),
|
||||||
|
}
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def _convert_gnocchi_resources_threshold_alarm_rule(cls, rule):
|
||||||
|
return {
|
||||||
|
CeilProps.RESOURCE_ID: _parse_query(rule, CeilProps.RESOURCE_ID),
|
||||||
|
}
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def _convert_event_alarm_rule(cls, rule):
|
||||||
|
return {
|
||||||
|
CeilProps.EVENT_TYPE: rule[CeilProps.EVENT_TYPE],
|
||||||
|
CeilProps.RESOURCE_ID:
|
||||||
|
_parse_query(rule, CeilProps.EVENT_RESOURCE_ID)
|
||||||
|
}
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def _convert_detail_event(cls, event):
|
||||||
|
alarm_info = event[CeilProps.DETAIL]
|
||||||
|
alarm_rule = alarm_info[CeilProps.RULE]
|
||||||
|
|
||||||
|
entity_detail = {
|
||||||
|
CeilProps.DESCRIPTION: alarm_info[CeilProps.DESCRIPTION],
|
||||||
|
CeilProps.ENABLED: alarm_info[CeilProps.ENABLED],
|
||||||
|
CeilProps.NAME: alarm_info[CeilProps.NAME],
|
||||||
|
CeilProps.STATE: alarm_info[CeilProps.STATE],
|
||||||
|
CeilProps.REPEAT_ACTIONS: alarm_info[CeilProps.REPEAT_ACTIONS],
|
||||||
|
CeilProps.TYPE: alarm_info[CeilProps.TYPE],
|
||||||
|
CeilProps.STATE_TIMESTAMP: alarm_info[CeilProps.STATE_TIMESTAMP],
|
||||||
|
CeilProps.STATE_REASON: alarm_info[CeilProps.STATE_REASON]
|
||||||
|
}
|
||||||
|
|
||||||
|
if _is_vitrage_alarm(alarm_rule):
|
||||||
|
entity_detail.update(cls._convert_vitrage_alarm_rule(alarm_rule))
|
||||||
|
elif entity_detail[CeilProps.TYPE] == CeilProps.EVENT:
|
||||||
|
entity_detail.update(cls._convert_event_alarm_rule(alarm_rule))
|
||||||
|
elif entity_detail[CeilProps.TYPE] == CeilProps.THRESHOLD:
|
||||||
|
entity_detail.update(
|
||||||
|
cls._convert_threshold_alarm_rule(alarm_rule))
|
||||||
|
elif entity_detail[CeilProps.TYPE] == \
|
||||||
|
CeilProps.GNOCCHI_RESOURCES_THRESHOLD:
|
||||||
|
entity_detail.update(
|
||||||
|
cls._convert_gnocchi_resources_threshold_alarm_rule(
|
||||||
|
alarm_rule))
|
||||||
|
|
||||||
|
return entity_detail
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def _parse_changed_rule(cls, change_rule):
|
||||||
|
entity = {}
|
||||||
|
if CeilProps.EVENT_TYPE in change_rule:
|
||||||
|
entity[CeilProps.EVENT_TYPE] = change_rule[CeilProps.EVENT_TYPE]
|
||||||
|
if 'query' in change_rule:
|
||||||
|
event_resource_id = \
|
||||||
|
_parse_query(change_rule, CeilProps.EVENT_RESOURCE_ID)
|
||||||
|
resource_id = \
|
||||||
|
_parse_query(change_rule, CeilProps.RESOURCE_ID)
|
||||||
|
if event_resource_id or resource_id:
|
||||||
|
entity[CeilProps.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"}}
|
||||||
|
"""
|
||||||
|
old_alarm = self._old_alarm(event)
|
||||||
|
entity = old_alarm.copy()
|
||||||
|
|
||||||
|
changed_rule = event[CeilProps.DETAIL]
|
||||||
|
for (changed_type, changed_info) in changed_rule.items():
|
||||||
|
# handle changed rule which may effect the neighbor
|
||||||
|
if changed_type == CeilProps.RULE:
|
||||||
|
entity.update(self._parse_changed_rule(
|
||||||
|
changed_rule[changed_type]))
|
||||||
|
# handle other changed alarm properties
|
||||||
|
elif changed_type in CeilProps.__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):
|
||||||
|
|
||||||
|
old_alarm = self._old_alarm(event)
|
||||||
|
entity = old_alarm.copy()
|
||||||
|
try:
|
||||||
|
entity[CeilProps.STATE] = event[CeilProps.DETAIL][CeilProps.STATE]
|
||||||
|
except Exception as e:
|
||||||
|
LOG.exception("Failed to Convert alarm state"
|
||||||
|
" transition event - %s", e)
|
||||||
|
|
||||||
|
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(CeilProps.QUERY, {})
|
||||||
|
for query in query_fields:
|
||||||
|
field = query['field']
|
||||||
|
if field == key:
|
||||||
|
return query['value']
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def _is_vitrage_alarm(rule):
|
||||||
|
return _parse_query(rule, CeilProps.VITRAGE_ID) is not None
|
53
vitrage/datasources/ceilometer/properties.py
Normal file
53
vitrage/datasources/ceilometer/properties.py
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
# 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.
|
||||||
|
|
||||||
|
|
||||||
|
class CeilometerProperties(object):
|
||||||
|
ALARM_ID = 'alarm_id'
|
||||||
|
DESCRIPTION = 'description'
|
||||||
|
ENABLED = 'enabled'
|
||||||
|
EVENT = 'event'
|
||||||
|
EVENT_TYPE = 'event_type'
|
||||||
|
EVENT_RESOURCE_ID = 'traits.resource_id'
|
||||||
|
NAME = 'name'
|
||||||
|
STATE = 'state'
|
||||||
|
PROJECT_ID = 'project_id'
|
||||||
|
QUERY = 'query'
|
||||||
|
REPEAT_ACTIONS = 'repeat_actions'
|
||||||
|
RESOURCE_ID = 'resource_id'
|
||||||
|
SEVERITY = 'severity'
|
||||||
|
STATE_TIMESTAMP = 'state_timestamp'
|
||||||
|
THRESHOLD = 'threshold'
|
||||||
|
GNOCCHI_RESOURCES_THRESHOLD = 'gnocchi_resources_threshold'
|
||||||
|
TIMESTAMP = 'timestamp'
|
||||||
|
TYPE = 'type'
|
||||||
|
VITRAGE_ID = 'vitrage_id'
|
||||||
|
DETAIL = 'detail'
|
||||||
|
RULE = 'rule'
|
||||||
|
GNOCCHI_RESOURCES_THRESHOLD_RULE = 'gnocchi_resources_threshold_rule'
|
||||||
|
USER_ID = 'user_id'
|
||||||
|
STATE_REASON = 'state_reason'
|
||||||
|
|
||||||
|
|
||||||
|
class CeilometerState(object):
|
||||||
|
OK = 'ok'
|
||||||
|
ALARM = 'alarm'
|
||||||
|
INSUFFICIENT_DATA = 'insufficient_data'
|
||||||
|
|
||||||
|
|
||||||
|
class CeilometerEventType(object):
|
||||||
|
CREATION = 'alarm.creation'
|
||||||
|
RULE_CHANGE = 'alarm.rule_change'
|
||||||
|
STATE_TRANSITION = 'alarm.state_transition'
|
||||||
|
DELETION = 'alarm.deletion'
|
167
vitrage/datasources/ceilometer/transformer.py
Normal file
167
vitrage/datasources/ceilometer/transformer.py
Normal file
@ -0,0 +1,167 @@
|
|||||||
|
# Copyright 2016 - Nokia
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||||
|
# not use this file except in compliance with the License. You may obtain
|
||||||
|
# a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
# License for the specific language governing permissions and limitations
|
||||||
|
# under the License.
|
||||||
|
|
||||||
|
from vitrage.common.constants import 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.ceilometer import CEILOMETER_DATASOURCE
|
||||||
|
from vitrage.datasources.ceilometer.properties \
|
||||||
|
import CeilometerEventType as CeilEventType
|
||||||
|
from vitrage.datasources.ceilometer.properties \
|
||||||
|
import CeilometerProperties as CeilProps
|
||||||
|
from vitrage.datasources.ceilometer.properties \
|
||||||
|
import CeilometerState as CeilState
|
||||||
|
from vitrage.datasources import transformer_base as tbase
|
||||||
|
from vitrage.datasources.transformer_base import Neighbor
|
||||||
|
from vitrage.datasources.transformer_base import TransformerBase
|
||||||
|
from vitrage.evaluator.actions.evaluator_event_transformer \
|
||||||
|
import VITRAGE_DATASOURCE
|
||||||
|
import vitrage.graph.utils as graph_utils
|
||||||
|
from vitrage.utils import datetime as datetime_utils
|
||||||
|
|
||||||
|
|
||||||
|
class CeilometerTransformer(AlarmTransformerBase):
|
||||||
|
|
||||||
|
# Event types which need to refer them differently
|
||||||
|
GRAPH_ACTION_MAPPING = {
|
||||||
|
CeilEventType.DELETION: GraphAction.DELETE_ENTITY,
|
||||||
|
}
|
||||||
|
|
||||||
|
def __init__(self, transformers, conf):
|
||||||
|
super(CeilometerTransformer, self).__init__(transformers, conf)
|
||||||
|
|
||||||
|
def _create_snapshot_entity_vertex(self, entity_event):
|
||||||
|
if _is_vitrage_alarm(entity_event):
|
||||||
|
return self._create_merge_alarm_vertex(entity_event)
|
||||||
|
return self._create_vertex(entity_event)
|
||||||
|
|
||||||
|
def _create_update_entity_vertex(self, entity_event):
|
||||||
|
if _is_vitrage_alarm(entity_event):
|
||||||
|
return self._create_merge_alarm_vertex(entity_event)
|
||||||
|
return self._create_vertex(entity_event)
|
||||||
|
|
||||||
|
def _create_vertex(self, entity_event):
|
||||||
|
metadata = {
|
||||||
|
VProps.NAME: entity_event[CeilProps.NAME],
|
||||||
|
VProps.SEVERITY: entity_event[CeilProps.SEVERITY],
|
||||||
|
CeilProps.DESCRIPTION: entity_event[CeilProps.DESCRIPTION],
|
||||||
|
CeilProps.ENABLED: entity_event[CeilProps.ENABLED],
|
||||||
|
VProps.PROJECT_ID: entity_event.get(CeilProps.PROJECT_ID, None),
|
||||||
|
CeilProps.REPEAT_ACTIONS: entity_event[CeilProps.REPEAT_ACTIONS],
|
||||||
|
VProps.RESOURCE_ID: entity_event[CeilProps.RESOURCE_ID],
|
||||||
|
'alarm_type': entity_event[CeilProps.TYPE]
|
||||||
|
}
|
||||||
|
|
||||||
|
# TODO(annarez): convert EVENT_TYPE to tuple
|
||||||
|
if entity_event[CeilProps.TYPE] == CeilProps.EVENT:
|
||||||
|
metadata[CeilProps.EVENT_TYPE] = \
|
||||||
|
entity_event[CeilProps.EVENT_TYPE]
|
||||||
|
|
||||||
|
elif entity_event[CeilProps.TYPE] == CeilProps.THRESHOLD:
|
||||||
|
metadata[CeilProps.STATE_TIMESTAMP] = \
|
||||||
|
entity_event[CeilProps.STATE_TIMESTAMP]
|
||||||
|
|
||||||
|
vitrage_sample_timestamp = entity_event[DSProps.SAMPLE_DATE]
|
||||||
|
|
||||||
|
update_timestamp = self._format_update_timestamp(
|
||||||
|
CeilometerTransformer._timestamp(entity_event),
|
||||||
|
vitrage_sample_timestamp)
|
||||||
|
|
||||||
|
return graph_utils.create_vertex(
|
||||||
|
self._create_entity_key(entity_event),
|
||||||
|
vitrage_category=EntityCategory.ALARM,
|
||||||
|
vitrage_type=entity_event[DSProps.ENTITY_TYPE],
|
||||||
|
vitrage_sample_timestamp=vitrage_sample_timestamp,
|
||||||
|
entity_id=entity_event[CeilProps.ALARM_ID],
|
||||||
|
entity_state=self._get_alarm_state(entity_event),
|
||||||
|
update_timestamp=update_timestamp,
|
||||||
|
metadata=metadata)
|
||||||
|
|
||||||
|
def _create_snapshot_neighbors(self, entity_event):
|
||||||
|
return self._create_aodh_neighbors(entity_event)
|
||||||
|
|
||||||
|
def _create_update_neighbors(self, entity_event):
|
||||||
|
return self._create_aodh_neighbors(entity_event)
|
||||||
|
|
||||||
|
def _create_aodh_neighbors(self, entity_event):
|
||||||
|
graph_neighbors = entity_event.get(self.QUERY_RESULT, [])
|
||||||
|
result = []
|
||||||
|
for vertex in graph_neighbors:
|
||||||
|
edge = graph_utils.create_edge(
|
||||||
|
source_id=TransformerBase.uuid_from_deprecated_vitrage_id(
|
||||||
|
self._create_entity_key(entity_event)),
|
||||||
|
target_id=vertex.vertex_id,
|
||||||
|
relationship_type=EdgeLabel.ON)
|
||||||
|
result.append(Neighbor(vertex, edge))
|
||||||
|
return result
|
||||||
|
|
||||||
|
def _create_merge_alarm_vertex(self, entity_event):
|
||||||
|
"""Handle an alarm that already has a vitrage_id
|
||||||
|
|
||||||
|
This is a deduced alarm created in aodh by vitrage, so it already
|
||||||
|
exists in the graph.
|
||||||
|
This function will update the exiting vertex (and not create a new one)
|
||||||
|
"""
|
||||||
|
metadata = {
|
||||||
|
CeilProps.DESCRIPTION: entity_event[CeilProps.DESCRIPTION],
|
||||||
|
VProps.PROJECT_ID: entity_event[CeilProps.PROJECT_ID],
|
||||||
|
}
|
||||||
|
vitrage_sample_timestamp = entity_event[DSProps.SAMPLE_DATE]
|
||||||
|
update_timestamp = self._format_update_timestamp(
|
||||||
|
CeilometerTransformer._timestamp(entity_event),
|
||||||
|
vitrage_sample_timestamp)
|
||||||
|
|
||||||
|
return graph_utils.create_vertex(
|
||||||
|
self._create_entity_key(entity_event),
|
||||||
|
vitrage_category=EntityCategory.ALARM,
|
||||||
|
vitrage_type=VITRAGE_DATASOURCE,
|
||||||
|
vitrage_sample_timestamp=vitrage_sample_timestamp,
|
||||||
|
entity_id=entity_event.get(CeilProps.ALARM_ID),
|
||||||
|
update_timestamp=update_timestamp,
|
||||||
|
metadata=metadata)
|
||||||
|
|
||||||
|
def _ok_status(self, entity_event):
|
||||||
|
return entity_event[CeilProps.STATE] != CeilState.ALARM
|
||||||
|
|
||||||
|
def _create_entity_key(self, entity_event):
|
||||||
|
if _is_vitrage_alarm(entity_event):
|
||||||
|
return entity_event.get(CeilProps.VITRAGE_ID)
|
||||||
|
|
||||||
|
entity_type = entity_event[DSProps.ENTITY_TYPE]
|
||||||
|
alarm_id = entity_event[CeilProps.ALARM_ID]
|
||||||
|
return tbase.build_key((EntityCategory.ALARM, entity_type, alarm_id))
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _timestamp(entity_event):
|
||||||
|
return datetime_utils.change_time_str_format(
|
||||||
|
entity_event[CeilProps.TIMESTAMP],
|
||||||
|
'%Y-%m-%dT%H:%M:%S.%f',
|
||||||
|
tbase.TIMESTAMP_FORMAT)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def get_enrich_query(event):
|
||||||
|
affected_resource_id = event.get(CeilProps.RESOURCE_ID, None)
|
||||||
|
if not affected_resource_id:
|
||||||
|
return None
|
||||||
|
return {VProps.ID: affected_resource_id}
|
||||||
|
|
||||||
|
def get_vitrage_type(self):
|
||||||
|
return CEILOMETER_DATASOURCE
|
||||||
|
|
||||||
|
|
||||||
|
def _is_vitrage_alarm(entity_event):
|
||||||
|
return entity_event.get(CeilProps.VITRAGE_ID) is not None
|
@ -41,7 +41,7 @@ class AodhNotifier(NotifierBase):
|
|||||||
|
|
||||||
def __init__(self, conf):
|
def __init__(self, conf):
|
||||||
super(AodhNotifier, self).__init__(conf)
|
super(AodhNotifier, self).__init__(conf)
|
||||||
self.client = os_clients.ceilometer_client(conf)
|
self.client = os_clients.aodh_client(conf)
|
||||||
|
|
||||||
def process_event(self, data, event_type):
|
def process_event(self, data, event_type):
|
||||||
response = None
|
response = None
|
||||||
@ -62,7 +62,7 @@ class AodhNotifier(NotifierBase):
|
|||||||
alarm_request = _alarm_request(alarm, state)
|
alarm_request = _alarm_request(alarm, state)
|
||||||
try:
|
try:
|
||||||
LOG.info('Aodh Alarm - Activate: ' + str(alarm_request))
|
LOG.info('Aodh Alarm - Activate: ' + str(alarm_request))
|
||||||
return self.client.alarms.create(**alarm_request)
|
return self.client.alarm.create(alarm_request)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
LOG.exception('Failed to activate Aodh Alarm Got Exception: %s', e)
|
LOG.exception('Failed to activate Aodh Alarm Got Exception: %s', e)
|
||||||
return
|
return
|
||||||
@ -71,7 +71,9 @@ class AodhNotifier(NotifierBase):
|
|||||||
aodh_id = alarm.get(VProps.ID)
|
aodh_id = alarm.get(VProps.ID)
|
||||||
try:
|
try:
|
||||||
LOG.info('Aodh Alarm $%s update state %s', aodh_id, state)
|
LOG.info('Aodh Alarm $%s update state %s', aodh_id, state)
|
||||||
return self.client.alarms.update(alarm_id=aodh_id, state=state)
|
alarm_update = {AodhState: state}
|
||||||
|
return self.client.alarm.update(alarm_id=aodh_id,
|
||||||
|
alarm_update=alarm_update)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
LOG.exception('Failed to update Aodh Alarm Got Exception: %s', e)
|
LOG.exception('Failed to update Aodh Alarm Got Exception: %s', e)
|
||||||
return
|
return
|
||||||
|
@ -22,6 +22,7 @@ LOG = log.getLogger(__name__)
|
|||||||
|
|
||||||
OPTS = [
|
OPTS = [
|
||||||
cfg.StrOpt('aodh_version', default='2', help='Aodh version'),
|
cfg.StrOpt('aodh_version', default='2', help='Aodh version'),
|
||||||
|
cfg.StrOpt('ceilometer_version', default='2', help='Ceilometer version'),
|
||||||
cfg.StrOpt('nova_version', default='2.11', help='Nova version'),
|
cfg.StrOpt('nova_version', default='2.11', help='Nova version'),
|
||||||
cfg.StrOpt('cinder_version', default='2', help='Cinder version'),
|
cfg.StrOpt('cinder_version', default='2', help='Cinder version'),
|
||||||
cfg.StrOpt('glance_version', default='2', help='Glance version'),
|
cfg.StrOpt('glance_version', default='2', help='Glance version'),
|
||||||
@ -30,6 +31,7 @@ OPTS = [
|
|||||||
]
|
]
|
||||||
|
|
||||||
_client_modules = {
|
_client_modules = {
|
||||||
|
'aodh': 'aodhclient.client',
|
||||||
'ceilometer': 'ceilometerclient.client',
|
'ceilometer': 'ceilometerclient.client',
|
||||||
'nova': 'novaclient.client',
|
'nova': 'novaclient.client',
|
||||||
'cinder': 'cinderclient.client',
|
'cinder': 'cinderclient.client',
|
||||||
@ -46,13 +48,26 @@ def driver_module(driver):
|
|||||||
return module
|
return module
|
||||||
|
|
||||||
|
|
||||||
|
def aodh_client(conf):
|
||||||
|
"""Get an instance of aodh client"""
|
||||||
|
try:
|
||||||
|
aodh_client = driver_module('aodh')
|
||||||
|
client = aodh_client.Client(
|
||||||
|
conf.aodh_version,
|
||||||
|
session=keystone_client.get_session(conf))
|
||||||
|
LOG.info('Aodh client created')
|
||||||
|
return client
|
||||||
|
except Exception as e:
|
||||||
|
LOG.exception('Create Aodh client - Got Exception: %s', e)
|
||||||
|
|
||||||
|
|
||||||
def ceilometer_client(conf):
|
def ceilometer_client(conf):
|
||||||
"""Get an instance of ceilometer client"""
|
"""Get an instance of ceilometer client"""
|
||||||
auth_config = conf.service_credentials
|
auth_config = conf.service_credentials
|
||||||
try:
|
try:
|
||||||
cm_client = driver_module('ceilometer')
|
cm_client = driver_module('ceilometer')
|
||||||
client = cm_client.get_client(
|
client = cm_client.get_client(
|
||||||
version=conf.aodh_version,
|
version=conf.ceilometer_version,
|
||||||
session=keystone_client.get_session(conf),
|
session=keystone_client.get_session(conf),
|
||||||
region_name=auth_config.region_name,
|
region_name=auth_config.region_name,
|
||||||
interface=auth_config.interface,
|
interface=auth_config.interface,
|
||||||
|
@ -14,6 +14,7 @@
|
|||||||
|
|
||||||
from oslo_config import cfg
|
from oslo_config import cfg
|
||||||
|
|
||||||
|
from vitrage.common.constants import DatasourceProperties as DSProp
|
||||||
from vitrage.common.constants import EntityCategory
|
from vitrage.common.constants import EntityCategory
|
||||||
from vitrage.common.constants import VertexProperties as VProps
|
from vitrage.common.constants import VertexProperties as VProps
|
||||||
from vitrage.datasources.aodh import AODH_DATASOURCE
|
from vitrage.datasources.aodh import AODH_DATASOURCE
|
||||||
@ -57,7 +58,8 @@ class TestAodhAlarms(TestDataSourcesBase):
|
|||||||
self.assertEqual(self._num_total_expected_vertices(),
|
self.assertEqual(self._num_total_expected_vertices(),
|
||||||
len(processor.entity_graph))
|
len(processor.entity_graph))
|
||||||
|
|
||||||
detail = {TransformerBase.QUERY_RESULT: ''}
|
detail = {TransformerBase.QUERY_RESULT: '',
|
||||||
|
DSProp.ENTITY_TYPE: AODH_DATASOURCE}
|
||||||
spec_list = \
|
spec_list = \
|
||||||
mock_transformer.simple_aodh_alarm_generators(alarm_num=1,
|
mock_transformer.simple_aodh_alarm_generators(alarm_num=1,
|
||||||
snapshot_events=1,
|
snapshot_events=1,
|
||||||
|
15
vitrage/tests/functional/datasources/ceilometer/__init__.py
Normal file
15
vitrage/tests/functional/datasources/ceilometer/__init__.py
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
# Copyright 2017 - ZTE
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||||
|
# not use this file except in compliance with the License. You may obtain
|
||||||
|
# a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
# License for the specific language governing permissions and limitations
|
||||||
|
# under the License.
|
||||||
|
|
||||||
|
__author__ = 'stack'
|
@ -0,0 +1,94 @@
|
|||||||
|
# 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 vitrage.common.constants import DatasourceProperties as DSProp
|
||||||
|
from vitrage.common.constants import EntityCategory
|
||||||
|
from vitrage.common.constants import VertexProperties as VProps
|
||||||
|
from vitrage.datasources.ceilometer import CEILOMETER_DATASOURCE
|
||||||
|
from vitrage.datasources.ceilometer.properties \
|
||||||
|
import CeilometerProperties as CeilProps
|
||||||
|
from vitrage.datasources import NOVA_HOST_DATASOURCE
|
||||||
|
from vitrage.datasources import NOVA_INSTANCE_DATASOURCE
|
||||||
|
from vitrage.datasources import NOVA_ZONE_DATASOURCE
|
||||||
|
from vitrage.datasources.transformer_base import TransformerBase
|
||||||
|
from vitrage.tests.functional.datasources.base import \
|
||||||
|
TestDataSourcesBase
|
||||||
|
from vitrage.tests.mocks import mock_transformer
|
||||||
|
|
||||||
|
|
||||||
|
class TestCeilometerAlarms(TestDataSourcesBase):
|
||||||
|
|
||||||
|
DATASOURCES_OPTS = [
|
||||||
|
cfg.ListOpt('types',
|
||||||
|
default=[CEILOMETER_DATASOURCE,
|
||||||
|
NOVA_HOST_DATASOURCE,
|
||||||
|
NOVA_INSTANCE_DATASOURCE,
|
||||||
|
NOVA_ZONE_DATASOURCE],
|
||||||
|
help='Names of supported driver data sources'),
|
||||||
|
|
||||||
|
cfg.ListOpt('path',
|
||||||
|
default=['vitrage.datasources'],
|
||||||
|
help='base path for data sources')
|
||||||
|
]
|
||||||
|
|
||||||
|
# noinspection PyPep8Naming
|
||||||
|
@classmethod
|
||||||
|
def setUpClass(cls):
|
||||||
|
super(TestCeilometerAlarms, cls).setUpClass()
|
||||||
|
cls.conf = cfg.ConfigOpts()
|
||||||
|
cls.conf.register_opts(cls.PROCESSOR_OPTS, group='entity_graph')
|
||||||
|
cls.conf.register_opts(cls.DATASOURCES_OPTS, group='datasources')
|
||||||
|
cls.load_datasources(cls.conf)
|
||||||
|
|
||||||
|
def test_ceilometer_alarms_validity(self):
|
||||||
|
# Setup
|
||||||
|
processor = self._create_processor_with_graph(self.conf)
|
||||||
|
self.assertEqual(self._num_total_expected_vertices(),
|
||||||
|
len(processor.entity_graph))
|
||||||
|
|
||||||
|
detail = {TransformerBase.QUERY_RESULT: '',
|
||||||
|
DSProp.ENTITY_TYPE: CEILOMETER_DATASOURCE}
|
||||||
|
spec_list = \
|
||||||
|
mock_transformer.simple_aodh_alarm_generators(alarm_num=1,
|
||||||
|
snapshot_events=1,
|
||||||
|
snap_vals=detail)
|
||||||
|
static_events = mock_transformer.generate_random_events_list(spec_list)
|
||||||
|
|
||||||
|
aodh_event = static_events[0]
|
||||||
|
aodh_event[CeilProps.RESOURCE_ID] = \
|
||||||
|
self._find_entity_id_by_type(processor.entity_graph,
|
||||||
|
NOVA_HOST_DATASOURCE)
|
||||||
|
|
||||||
|
# Action
|
||||||
|
processor.process_event(aodh_event)
|
||||||
|
|
||||||
|
# Test assertions
|
||||||
|
self.assertEqual(self._num_total_expected_vertices() + 1,
|
||||||
|
len(processor.entity_graph))
|
||||||
|
|
||||||
|
aodh_vertices = processor.entity_graph.get_vertices(
|
||||||
|
vertex_attr_filter={
|
||||||
|
VProps.VITRAGE_CATEGORY: EntityCategory.ALARM,
|
||||||
|
VProps.VITRAGE_TYPE: CEILOMETER_DATASOURCE
|
||||||
|
})
|
||||||
|
self.assertEqual(1, len(aodh_vertices))
|
||||||
|
|
||||||
|
aodh_neighbors = processor.entity_graph.neighbors(
|
||||||
|
aodh_vertices[0].vertex_id)
|
||||||
|
self.assertEqual(1, len(aodh_neighbors))
|
||||||
|
|
||||||
|
self.assertEqual(NOVA_HOST_DATASOURCE,
|
||||||
|
aodh_neighbors[0][VProps.VITRAGE_TYPE])
|
15
vitrage/tests/unit/datasources/ceilometer/__init__.py
Normal file
15
vitrage/tests/unit/datasources/ceilometer/__init__.py
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
# Copyright 2017 - ZTE
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||||
|
# not use this file except in compliance with the License. You may obtain
|
||||||
|
# a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
# License for the specific language governing permissions and limitations
|
||||||
|
# under the License.
|
||||||
|
|
||||||
|
__author__ = 'stack'
|
@ -0,0 +1,119 @@
|
|||||||
|
# Copyright 2017 - 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.ceilometer.properties \
|
||||||
|
import CeilometerProperties as CeilProps
|
||||||
|
from vitrage.datasources.ceilometer.properties import CeilometerState
|
||||||
|
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 CeilometerTransformerBaseTest(base.BaseTest):
|
||||||
|
|
||||||
|
def _validate_aodh_vertex_props(self, vertex, event):
|
||||||
|
|
||||||
|
self.assertEqual(EntityCategory.ALARM,
|
||||||
|
vertex[VProps.VITRAGE_CATEGORY])
|
||||||
|
self.assertEqual(event[DSProps.ENTITY_TYPE],
|
||||||
|
vertex[VProps.VITRAGE_TYPE])
|
||||||
|
self.assertEqual(event[CeilProps.NAME], vertex[VProps.NAME])
|
||||||
|
self.assertEqual(event[CeilProps.SEVERITY],
|
||||||
|
vertex[VProps.SEVERITY])
|
||||||
|
self.assertEqual(event[CeilProps.DESCRIPTION],
|
||||||
|
vertex[CeilProps.DESCRIPTION])
|
||||||
|
self.assertEqual(event[CeilProps.ENABLED],
|
||||||
|
vertex[CeilProps.ENABLED])
|
||||||
|
self.assertEqual(event[CeilProps.PROJECT_ID],
|
||||||
|
vertex[VProps.PROJECT_ID])
|
||||||
|
self.assertEqual(event[CeilProps.REPEAT_ACTIONS],
|
||||||
|
vertex[CeilProps.REPEAT_ACTIONS])
|
||||||
|
self.assertEqual(event[CeilProps.TYPE], vertex['alarm_type'])
|
||||||
|
if event[CeilProps.TYPE] == CeilProps.EVENT:
|
||||||
|
self.assertEqual(event[CeilProps.EVENT_TYPE],
|
||||||
|
vertex[CeilProps.EVENT_TYPE])
|
||||||
|
elif event[CeilProps.TYPE] == CeilProps.THRESHOLD:
|
||||||
|
self.assertEqual(event[CeilProps.STATE_TIMESTAMP],
|
||||||
|
vertex[CeilProps.STATE_TIMESTAMP])
|
||||||
|
self.assertEqual(event[DSProps.SAMPLE_DATE],
|
||||||
|
vertex[VProps.VITRAGE_SAMPLE_TIMESTAMP])
|
||||||
|
|
||||||
|
event_status = event[CeilProps.STATE]
|
||||||
|
if event_status == CeilometerState.OK:
|
||||||
|
self.assertEqual(AlarmProps.INACTIVE_STATE,
|
||||||
|
vertex[VProps.STATE])
|
||||||
|
else:
|
||||||
|
self.assertEqual(AlarmProps.ACTIVE_STATE,
|
||||||
|
vertex[VProps.STATE])
|
||||||
|
self.assertFalse(vertex[VProps.VITRAGE_IS_PLACEHOLDER])
|
||||||
|
self.assertFalse(vertex[VProps.VITRAGE_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[CeilProps.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.VITRAGE_CATEGORY])
|
||||||
|
self.assertEqual(NOVA_INSTANCE_DATASOURCE,
|
||||||
|
alarm_neighbor.vertex[VProps.VITRAGE_TYPE])
|
||||||
|
self.assertEqual(resource_id, alarm_neighbor.vertex[VProps.ID])
|
||||||
|
self.assertFalse(alarm_neighbor.vertex[VProps.VITRAGE_IS_PLACEHOLDER])
|
||||||
|
self.assertFalse(alarm_neighbor.vertex[VProps.VITRAGE_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.VITRAGE_CATEGORY] + \
|
||||||
|
TransformerBase.KEY_SEPARATOR + neighbor[VProps.VITRAGE_TYPE] + \
|
||||||
|
TransformerBase.KEY_SEPARATOR + neighbor[VProps.ID]
|
||||||
|
return Vertex(vertex_id=ver_id, properties=neighbor)
|
27
vitrage/tests/unit/datasources/ceilometer/mock_driver.py
Normal file
27
vitrage/tests/unit/datasources/ceilometer/mock_driver.py
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
# Copyright 2017 - Nokia
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||||
|
# not use this file except in compliance with the License. You may obtain
|
||||||
|
# a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
# License for the specific language governing permissions and limitations
|
||||||
|
# under the License.
|
||||||
|
|
||||||
|
from vitrage.datasources.ceilometer.driver import CeilometerDriver
|
||||||
|
|
||||||
|
|
||||||
|
class MockCeilometerDriver(CeilometerDriver):
|
||||||
|
"""A aodh driver for tests.
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, conf):
|
||||||
|
super(MockCeilometerDriver, self).__init__(conf)
|
||||||
|
|
||||||
|
def _cache_all_alarms(self):
|
||||||
|
pass
|
@ -0,0 +1,389 @@
|
|||||||
|
# Copyright 2017 - 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 vitrage.common.constants import DatasourceOpts as DSOpts
|
||||||
|
from vitrage.common.constants import DatasourceProperties as DSProps
|
||||||
|
from vitrage.common.constants import UpdateMethod
|
||||||
|
from vitrage.datasources.ceilometer import CEILOMETER_DATASOURCE
|
||||||
|
from vitrage.datasources.ceilometer.properties import CeilometerEventType
|
||||||
|
from vitrage.datasources.ceilometer.properties \
|
||||||
|
import CeilometerProperties as CeilProps
|
||||||
|
from vitrage.tests import base
|
||||||
|
from vitrage.tests.mocks import mock_driver
|
||||||
|
from vitrage.tests.unit.datasources.ceilometer.mock_driver \
|
||||||
|
import MockCeilometerDriver
|
||||||
|
|
||||||
|
|
||||||
|
class CeilometerDriverTest(base.BaseTest):
|
||||||
|
|
||||||
|
OPTS = [
|
||||||
|
cfg.StrOpt(DSOpts.UPDATE_METHOD,
|
||||||
|
default=UpdateMethod.PUSH),
|
||||||
|
]
|
||||||
|
|
||||||
|
# noinspection PyPep8Naming
|
||||||
|
@classmethod
|
||||||
|
def setUpClass(cls):
|
||||||
|
cls.conf = cfg.ConfigOpts()
|
||||||
|
cls.conf.register_opts(cls.OPTS, group=CEILOMETER_DATASOURCE)
|
||||||
|
|
||||||
|
def test_event_alarm_notifications(self):
|
||||||
|
|
||||||
|
aodh_driver = MockCeilometerDriver(self.conf)
|
||||||
|
|
||||||
|
# 1. alarm creation with 'ok' state
|
||||||
|
# prepare data
|
||||||
|
detail_data = {"type": "creation",
|
||||||
|
CeilProps.DETAIL: self._create_alarm_data_type_event(),
|
||||||
|
}
|
||||||
|
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, CeilometerEventType.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",
|
||||||
|
CeilProps.DETAIL: {CeilProps.STATE: "alarm"}}
|
||||||
|
alarm.update(detail_data)
|
||||||
|
entity = aodh_driver.enrich_event(alarm,
|
||||||
|
CeilometerEventType.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[CeilProps.STATE],
|
||||||
|
alarm[CeilProps.DETAIL][CeilProps.STATE])
|
||||||
|
self.assertEqual(entity[CeilProps.SEVERITY],
|
||||||
|
alarm[CeilProps.SEVERITY])
|
||||||
|
self.assertEqual(entity[DSProps.EVENT_TYPE],
|
||||||
|
CeilometerEventType.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, CeilometerEventType.DELETION)
|
||||||
|
|
||||||
|
# Test assertions
|
||||||
|
self.assertIsNotNone(entity)
|
||||||
|
self._validate_aodh_entity_comm_props(entity, alarm_info)
|
||||||
|
self.assertEqual(entity[DSProps.EVENT_TYPE],
|
||||||
|
CeilometerEventType.DELETION)
|
||||||
|
|
||||||
|
# 4. alarm creation with 'alarm' state
|
||||||
|
# prepare data
|
||||||
|
detail_data = {"type": "creation",
|
||||||
|
CeilProps.DETAIL:
|
||||||
|
self._create_alarm_data_type_event(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, CeilometerEventType.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[CeilProps.STATE],
|
||||||
|
alarm[CeilProps.DETAIL][CeilProps.STATE])
|
||||||
|
self.assertEqual(entity[CeilProps.SEVERITY],
|
||||||
|
alarm[CeilProps.SEVERITY])
|
||||||
|
self.assertIsNone(entity[CeilProps.RESOURCE_ID])
|
||||||
|
self.assertEqual("*", entity[CeilProps.EVENT_TYPE])
|
||||||
|
self.assertEqual(entity[DSProps.EVENT_TYPE],
|
||||||
|
CeilometerEventType.CREATION)
|
||||||
|
|
||||||
|
# 5. alarm rule change
|
||||||
|
# prepare data
|
||||||
|
detail_data = {"type": "rule change",
|
||||||
|
CeilProps.DETAIL: {
|
||||||
|
"severity": "critical",
|
||||||
|
CeilProps.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, CeilometerEventType.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[CeilProps.SEVERITY],
|
||||||
|
alarm[CeilProps.DETAIL][CeilProps.SEVERITY])
|
||||||
|
self.assertEqual(
|
||||||
|
entity[CeilProps.EVENT_TYPE],
|
||||||
|
alarm[CeilProps.DETAIL][CeilProps.RULE][CeilProps.EVENT_TYPE])
|
||||||
|
self.assertEqual("1", entity[CeilProps.RESOURCE_ID])
|
||||||
|
self.assertEqual(entity[DSProps.EVENT_TYPE],
|
||||||
|
CeilometerEventType.RULE_CHANGE)
|
||||||
|
|
||||||
|
# 6. alarm state change from 'alarm' to 'ok'
|
||||||
|
# prepare data
|
||||||
|
detail_data = {"type": "state transition",
|
||||||
|
CeilProps.DETAIL: {CeilProps.STATE: "ok"}}
|
||||||
|
alarm.update(detail_data)
|
||||||
|
|
||||||
|
# action
|
||||||
|
entity = aodh_driver.enrich_event(
|
||||||
|
alarm, CeilometerEventType.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],
|
||||||
|
CeilometerEventType.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, CeilometerEventType.DELETION)
|
||||||
|
|
||||||
|
# Test assertions
|
||||||
|
self.assertIsNone(entity)
|
||||||
|
|
||||||
|
def test_gnocchi_threshold_alarm_notifications(self):
|
||||||
|
aodh_driver = MockCeilometerDriver(self.conf)
|
||||||
|
|
||||||
|
# 1. alarm creation with 'ok' state
|
||||||
|
# prepare data
|
||||||
|
detail_data = {"type": "gnocchi_resources_threshold",
|
||||||
|
CeilProps.DETAIL: self._create_alarm_data_gnocchi()}
|
||||||
|
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, CeilometerEventType.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",
|
||||||
|
CeilProps.DETAIL: {CeilProps.STATE: "alarm"}}
|
||||||
|
alarm.update(detail_data)
|
||||||
|
entity = aodh_driver.enrich_event(
|
||||||
|
alarm, CeilometerEventType.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[CeilProps.STATE],
|
||||||
|
alarm[CeilProps.DETAIL][CeilProps.STATE])
|
||||||
|
self.assertEqual(entity[CeilProps.SEVERITY],
|
||||||
|
alarm[CeilProps.SEVERITY])
|
||||||
|
|
||||||
|
# 3. delete alarm which is 'alarm' state
|
||||||
|
# prepare data
|
||||||
|
detail_data = {"type": "deletion"}
|
||||||
|
alarm.update(detail_data)
|
||||||
|
|
||||||
|
# action
|
||||||
|
entity = aodh_driver.enrich_event(
|
||||||
|
alarm, CeilometerEventType.DELETION)
|
||||||
|
|
||||||
|
# Test assertions
|
||||||
|
self.assertIsNotNone(entity)
|
||||||
|
self._validate_aodh_entity_comm_props(entity, alarm_info)
|
||||||
|
self.assertEqual(entity[DSProps.EVENT_TYPE],
|
||||||
|
CeilometerEventType.DELETION)
|
||||||
|
|
||||||
|
# 4. alarm creation with 'alarm' state
|
||||||
|
# prepare data
|
||||||
|
detail_data = {"type": "gnocchi_resources_threshold",
|
||||||
|
CeilProps.DETAIL:
|
||||||
|
self._create_alarm_data_gnocchi(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, CeilometerEventType.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[CeilProps.STATE],
|
||||||
|
alarm[CeilProps.DETAIL][CeilProps.STATE])
|
||||||
|
self.assertEqual(entity[CeilProps.SEVERITY],
|
||||||
|
alarm[CeilProps.SEVERITY])
|
||||||
|
self.assertEqual(entity[DSProps.EVENT_TYPE],
|
||||||
|
CeilometerEventType.CREATION)
|
||||||
|
|
||||||
|
# 5. alarm rule change
|
||||||
|
# prepare data
|
||||||
|
detail_data = {"type": "rule change",
|
||||||
|
CeilProps.DETAIL: {
|
||||||
|
"severity": "critical",
|
||||||
|
CeilProps.RULE:
|
||||||
|
{"granularity": "300",
|
||||||
|
"threshold": "0.0123",
|
||||||
|
"comparison_operator": "eq"}}}
|
||||||
|
alarm.update(detail_data)
|
||||||
|
|
||||||
|
# action
|
||||||
|
entity = aodh_driver.enrich_event(
|
||||||
|
alarm, CeilometerEventType.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[CeilProps.SEVERITY],
|
||||||
|
alarm[CeilProps.DETAIL][CeilProps.SEVERITY])
|
||||||
|
self.assertEqual(entity[DSProps.EVENT_TYPE],
|
||||||
|
CeilometerEventType.RULE_CHANGE)
|
||||||
|
|
||||||
|
# 6. alarm state change from 'alarm' to 'ok'
|
||||||
|
# prepare data
|
||||||
|
detail_data = {"type": "state transition",
|
||||||
|
CeilProps.DETAIL: {CeilProps.STATE: "ok"}}
|
||||||
|
alarm.update(detail_data)
|
||||||
|
|
||||||
|
# action
|
||||||
|
entity = aodh_driver.enrich_event(
|
||||||
|
alarm,
|
||||||
|
CeilometerEventType.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],
|
||||||
|
CeilometerEventType.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, CeilometerEventType.DELETION)
|
||||||
|
|
||||||
|
# Test assertions
|
||||||
|
self.assertIsNone(entity)
|
||||||
|
|
||||||
|
def _create_alarm_data_gnocchi(self,
|
||||||
|
state="ok",
|
||||||
|
type="gnocchi_resources_threshold",
|
||||||
|
rule=None):
|
||||||
|
|
||||||
|
if rule is None:
|
||||||
|
rule = {"granularity": "300",
|
||||||
|
"threshold": "0.001",
|
||||||
|
"comparison_operator": "gt",
|
||||||
|
"resource_type": "instance"
|
||||||
|
}
|
||||||
|
return {CeilProps.DESCRIPTION: "test",
|
||||||
|
CeilProps.TIMESTAMP: "2016-11-09T01:39:13.839584",
|
||||||
|
CeilProps.ENABLED: True,
|
||||||
|
CeilProps.STATE_TIMESTAMP: "2016-11-09T01:39:13.839584",
|
||||||
|
CeilProps.ALARM_ID: "7e5c3754-e2eb-4782-ae00-7da5ded8568b",
|
||||||
|
CeilProps.REPEAT_ACTIONS: False,
|
||||||
|
CeilProps.PROJECT_ID: "c365d18fcc03493187016ae743f0cc4d",
|
||||||
|
CeilProps.NAME: "test",
|
||||||
|
CeilProps.SEVERITY: "low",
|
||||||
|
CeilProps.RESOURCE_ID: "88cd2d1d-8af4-4d00-9b5e-f82f8c8b0f8d",
|
||||||
|
CeilProps.TYPE: type,
|
||||||
|
CeilProps.STATE: state,
|
||||||
|
CeilProps.RULE: rule,
|
||||||
|
CeilProps.STATE_REASON: 'for test'}
|
||||||
|
|
||||||
|
def _create_alarm_data_type_event(self,
|
||||||
|
state="ok",
|
||||||
|
type="event",
|
||||||
|
rule=None):
|
||||||
|
|
||||||
|
if rule is None:
|
||||||
|
rule = {"query": [], "event_type": "*"}
|
||||||
|
return {CeilProps.DESCRIPTION: "test",
|
||||||
|
CeilProps.TIMESTAMP: "2016-11-09T01:39:13.839584",
|
||||||
|
CeilProps.ENABLED: True,
|
||||||
|
CeilProps.STATE_TIMESTAMP: "2016-11-09T01:39:13.839584",
|
||||||
|
CeilProps.ALARM_ID: "7e5c3754-e2eb-4782-ae00-7da5ded8568b",
|
||||||
|
CeilProps.REPEAT_ACTIONS: False,
|
||||||
|
CeilProps.PROJECT_ID: "c365d18fcc03493187016ae743f0cc4d",
|
||||||
|
CeilProps.NAME: "test",
|
||||||
|
CeilProps.SEVERITY: "low",
|
||||||
|
CeilProps.TYPE: type,
|
||||||
|
CeilProps.STATE: state,
|
||||||
|
CeilProps.RULE: rule,
|
||||||
|
CeilProps.STATE_REASON: 'for test'}
|
||||||
|
|
||||||
|
def _validate_aodh_entity_comm_props(self, entity, alarm):
|
||||||
|
|
||||||
|
self.assertEqual(entity[CeilProps.ALARM_ID],
|
||||||
|
alarm[CeilProps.ALARM_ID])
|
||||||
|
self.assertEqual(entity[CeilProps.PROJECT_ID],
|
||||||
|
alarm[CeilProps.PROJECT_ID])
|
||||||
|
self.assertEqual(entity[CeilProps.TIMESTAMP],
|
||||||
|
alarm[CeilProps.TIMESTAMP])
|
||||||
|
self.assertEqual(entity[CeilProps.DESCRIPTION],
|
||||||
|
alarm[CeilProps.DETAIL][CeilProps.DESCRIPTION])
|
||||||
|
self.assertEqual(entity[CeilProps.ENABLED],
|
||||||
|
alarm[CeilProps.DETAIL][CeilProps.ENABLED])
|
||||||
|
self.assertEqual(entity[CeilProps.NAME],
|
||||||
|
alarm[CeilProps.DETAIL][CeilProps.NAME])
|
||||||
|
self.assertEqual(entity[CeilProps.REPEAT_ACTIONS],
|
||||||
|
alarm[CeilProps.DETAIL][CeilProps.REPEAT_ACTIONS])
|
||||||
|
self.assertEqual(entity[CeilProps.TYPE],
|
||||||
|
alarm[CeilProps.DETAIL][CeilProps.TYPE])
|
@ -0,0 +1,161 @@
|
|||||||
|
# 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 DatasourceOpts as DSOpts
|
||||||
|
from vitrage.common.constants import DatasourceProperties as DSProps
|
||||||
|
from vitrage.common.constants import EntityCategory
|
||||||
|
from vitrage.common.constants import UpdateMethod
|
||||||
|
from vitrage.datasources.ceilometer import CEILOMETER_DATASOURCE
|
||||||
|
from vitrage.datasources.ceilometer.properties \
|
||||||
|
import CeilometerProperties as CeilProps
|
||||||
|
from vitrage.datasources.ceilometer.transformer import CeilometerTransformer
|
||||||
|
from vitrage.datasources.transformer_base import TransformerBase
|
||||||
|
from vitrage.tests.mocks import mock_transformer as mock_sync
|
||||||
|
from vitrage.tests.unit.datasources.ceilometer.\
|
||||||
|
ceilometer_transformer_base_test \
|
||||||
|
import CeilometerTransformerBaseTest
|
||||||
|
|
||||||
|
LOG = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
class TestCeilometerAlarmTransformer(CeilometerTransformerBaseTest):
|
||||||
|
|
||||||
|
OPTS = [
|
||||||
|
cfg.StrOpt(DSOpts.UPDATE_METHOD,
|
||||||
|
default=UpdateMethod.PULL),
|
||||||
|
]
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def setUpClass(cls):
|
||||||
|
cls.transformers = {}
|
||||||
|
cls.conf = cfg.ConfigOpts()
|
||||||
|
cls.conf.register_opts(cls.OPTS, group=CEILOMETER_DATASOURCE)
|
||||||
|
cls.transformers[CEILOMETER_DATASOURCE] = \
|
||||||
|
CeilometerTransformer(cls.transformers, cls.conf)
|
||||||
|
|
||||||
|
def test_key_values_with_vitrage_alarm(self):
|
||||||
|
LOG.debug('Ceilometer transformer test: '
|
||||||
|
'get key values(vitrage_alarm)')
|
||||||
|
|
||||||
|
# Test setup
|
||||||
|
entity = {CeilProps.VITRAGE_ID: 'test',
|
||||||
|
DSProps.ENTITY_TYPE: CEILOMETER_DATASOURCE,
|
||||||
|
CeilProps.ALARM_ID: '12345'}
|
||||||
|
transformer = self.transformers[CEILOMETER_DATASOURCE]
|
||||||
|
|
||||||
|
# Test action
|
||||||
|
observed_key_fields = transformer._create_entity_key(entity)
|
||||||
|
|
||||||
|
# Test assertions
|
||||||
|
self.assertEqual('test', observed_key_fields)
|
||||||
|
|
||||||
|
def test_key_values(self):
|
||||||
|
LOG.debug('Ceilometer transformer test: get key values(aodh alarm)')
|
||||||
|
|
||||||
|
# Test setup
|
||||||
|
entity = {DSProps.ENTITY_TYPE: CEILOMETER_DATASOURCE,
|
||||||
|
CeilProps.ALARM_ID: '12345'}
|
||||||
|
transformer = self.transformers[CEILOMETER_DATASOURCE]
|
||||||
|
|
||||||
|
# Test action
|
||||||
|
entity_key_fields = transformer._create_entity_key(entity).split(":")
|
||||||
|
|
||||||
|
# Test assertions
|
||||||
|
self.assertEqual(EntityCategory.ALARM, entity_key_fields[0])
|
||||||
|
self.assertEqual(CEILOMETER_DATASOURCE, entity_key_fields[1])
|
||||||
|
self.assertEqual(entity[CeilProps.ALARM_ID], entity_key_fields[2])
|
||||||
|
|
||||||
|
def test_snapshot_transform(self):
|
||||||
|
LOG.debug('Ceilometer alarm transformer test: '
|
||||||
|
'transform entity event snapshot')
|
||||||
|
|
||||||
|
# Test setup
|
||||||
|
spec_list = mock_sync.simple_aodh_alarm_generators(alarm_num=3,
|
||||||
|
snapshot_events=3)
|
||||||
|
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:
|
||||||
|
neighbor_vertex = self._convert_dist_to_vertex(neighbor)
|
||||||
|
vertices.append(self.transformers[CEILOMETER_DATASOURCE].
|
||||||
|
update_uuid_in_vertex(neighbor_vertex))
|
||||||
|
event[TransformerBase.QUERY_RESULT] = vertices
|
||||||
|
|
||||||
|
# Test action
|
||||||
|
wrapper = self.transformers[CEILOMETER_DATASOURCE].transform(event)
|
||||||
|
|
||||||
|
# Test assertions
|
||||||
|
vertex = wrapper.vertex
|
||||||
|
self._validate_aodh_vertex_props(vertex, event)
|
||||||
|
|
||||||
|
neighbors = wrapper.neighbors
|
||||||
|
self.assertEqual(1, len(neighbors))
|
||||||
|
self._validate_neighbors(neighbors, vertex.vertex_id, event)
|
||||||
|
|
||||||
|
self._validate_action(event, wrapper)
|
||||||
|
|
||||||
|
|
||||||
|
class TestCeilometerAlarmPushTransformer(CeilometerTransformerBaseTest):
|
||||||
|
|
||||||
|
OPTS = [
|
||||||
|
cfg.StrOpt(DSOpts.UPDATE_METHOD,
|
||||||
|
default=UpdateMethod.PUSH),
|
||||||
|
]
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def setUpClass(cls):
|
||||||
|
cls.transformers = {}
|
||||||
|
cls.conf = cfg.ConfigOpts()
|
||||||
|
cls.conf.register_opts(cls.OPTS, group=CEILOMETER_DATASOURCE)
|
||||||
|
cls.transformers[CEILOMETER_DATASOURCE] = \
|
||||||
|
CeilometerTransformer(cls.transformers, cls.conf)
|
||||||
|
|
||||||
|
def test_update_transform(self):
|
||||||
|
LOG.debug('Ceilometer update alarm transformer test:'
|
||||||
|
'transform entity event update')
|
||||||
|
|
||||||
|
# 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 event in static_events:
|
||||||
|
# convert neighbor from dict to vertex object
|
||||||
|
neighbors = event[TransformerBase.QUERY_RESULT]
|
||||||
|
vertices = []
|
||||||
|
for neighbor in neighbors:
|
||||||
|
neighbor_vertex = self._convert_dist_to_vertex(neighbor)
|
||||||
|
vertices.append(self.transformers[CEILOMETER_DATASOURCE].
|
||||||
|
update_uuid_in_vertex(neighbor_vertex))
|
||||||
|
event[TransformerBase.QUERY_RESULT] = vertices
|
||||||
|
|
||||||
|
# Test action
|
||||||
|
wrapper = self.transformers[CEILOMETER_DATASOURCE].transform(event)
|
||||||
|
|
||||||
|
# Test assertions
|
||||||
|
vertex = wrapper.vertex
|
||||||
|
self._validate_aodh_vertex_props(vertex, event)
|
||||||
|
|
||||||
|
neighbors = wrapper.neighbors
|
||||||
|
self.assertEqual(1, len(neighbors))
|
||||||
|
self._validate_neighbors(neighbors, vertex.vertex_id, event)
|
||||||
|
|
||||||
|
self._validate_action(event, wrapper)
|
@ -12,9 +12,11 @@
|
|||||||
# License for the specific language governing permissions and limitations
|
# License for the specific language governing permissions and limitations
|
||||||
# under the License.
|
# under the License.
|
||||||
|
|
||||||
|
from vitrage.datasources.aodh.properties import AodhProperties as AodhProps
|
||||||
from vitrage_tempest_tests.tests.base import BaseVitrageTempest
|
from vitrage_tempest_tests.tests.base import BaseVitrageTempest
|
||||||
from vitrage_tempest_tests.tests.common.tempest_clients import TempestClients
|
from vitrage_tempest_tests.tests.common.tempest_clients import TempestClients
|
||||||
|
|
||||||
|
|
||||||
TEMPLATES_RESOURCES_PATH = 'resources/templates/'
|
TEMPLATES_RESOURCES_PATH = 'resources/templates/'
|
||||||
TEMPLATES_SOURCES_PATH = '/etc/vitrage/templates/'
|
TEMPLATES_SOURCES_PATH = '/etc/vitrage/templates/'
|
||||||
|
|
||||||
@ -27,8 +29,8 @@ class BaseAlarmsTest(BaseVitrageTempest):
|
|||||||
super(BaseAlarmsTest, cls).setUpClass()
|
super(BaseAlarmsTest, cls).setUpClass()
|
||||||
|
|
||||||
def _check_num_alarms(self, num_alarms=0, state=''):
|
def _check_num_alarms(self, num_alarms=0, state=''):
|
||||||
if len(TempestClients.ceilometer().alarms.list()) != num_alarms:
|
if len(TempestClients.aodh().alarm.list()) != num_alarms:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
return all(alarm.state.upper() == state.upper()
|
return all(alarm[AodhProps.STATE].upper() == state.upper()
|
||||||
for alarm in TempestClients.ceilometer().alarms.list())
|
for alarm in TempestClients.aodh().alarm.list())
|
||||||
|
@ -19,7 +19,7 @@ from oslo_log import log as logging
|
|||||||
from vitrage.common.constants import VertexProperties as VProps
|
from vitrage.common.constants import VertexProperties as VProps
|
||||||
from vitrage.datasources.aodh import AODH_DATASOURCE
|
from vitrage.datasources.aodh import AODH_DATASOURCE
|
||||||
from vitrage_tempest_tests.tests.api.alarms.base import BaseAlarmsTest
|
from vitrage_tempest_tests.tests.api.alarms.base import BaseAlarmsTest
|
||||||
from vitrage_tempest_tests.tests.common import ceilometer_utils
|
from vitrage_tempest_tests.tests.common import aodh_utils
|
||||||
from vitrage_tempest_tests.tests.common import nova_utils
|
from vitrage_tempest_tests.tests.common import nova_utils
|
||||||
from vitrage_tempest_tests.tests.common.tempest_clients import TempestClients
|
from vitrage_tempest_tests.tests.common.tempest_clients import TempestClients
|
||||||
from vitrage_tempest_tests.tests import utils
|
from vitrage_tempest_tests.tests import utils
|
||||||
@ -41,7 +41,7 @@ class TestAlarms(BaseAlarmsTest):
|
|||||||
instances = nova_utils.create_instances(num_instances=1)
|
instances = nova_utils.create_instances(num_instances=1)
|
||||||
self.assertNotEqual(len(instances), 0,
|
self.assertNotEqual(len(instances), 0,
|
||||||
'The instances list is empty')
|
'The instances list is empty')
|
||||||
ceilometer_utils.create_ceilometer_alarm(
|
aodh_utils.create_aodh_alarm(
|
||||||
resource_id=instances[0].id,
|
resource_id=instances[0].id,
|
||||||
name='tempest_aodh_test')
|
name='tempest_aodh_test')
|
||||||
|
|
||||||
@ -55,7 +55,7 @@ class TestAlarms(BaseAlarmsTest):
|
|||||||
self._handle_exception(e)
|
self._handle_exception(e)
|
||||||
raise
|
raise
|
||||||
finally:
|
finally:
|
||||||
ceilometer_utils.delete_all_ceilometer_alarms()
|
aodh_utils.delete_all_ceilometer_alarms()
|
||||||
nova_utils.delete_all_instances()
|
nova_utils.delete_all_instances()
|
||||||
|
|
||||||
def _compare_alarms_lists(self, api_alarms, cli_alarms,
|
def _compare_alarms_lists(self, api_alarms, cli_alarms,
|
||||||
|
@ -29,7 +29,7 @@ from vitrage.entity_graph.mappings.operational_resource_state \
|
|||||||
from vitrage.evaluator.actions.evaluator_event_transformer \
|
from vitrage.evaluator.actions.evaluator_event_transformer \
|
||||||
import VITRAGE_DATASOURCE
|
import VITRAGE_DATASOURCE
|
||||||
from vitrage_tempest_tests.tests.api.alarms.base import BaseAlarmsTest
|
from vitrage_tempest_tests.tests.api.alarms.base import BaseAlarmsTest
|
||||||
from vitrage_tempest_tests.tests.common import ceilometer_utils
|
from vitrage_tempest_tests.tests.common import aodh_utils
|
||||||
from vitrage_tempest_tests.tests.common import nova_utils
|
from vitrage_tempest_tests.tests.common import nova_utils
|
||||||
from vitrage_tempest_tests.tests.common import vitrage_utils
|
from vitrage_tempest_tests.tests.common import vitrage_utils
|
||||||
from vitrage_tempest_tests.tests import utils
|
from vitrage_tempest_tests.tests import utils
|
||||||
@ -47,10 +47,10 @@ class BaseRcaTest(BaseAlarmsTest):
|
|||||||
|
|
||||||
def _clean_all(self):
|
def _clean_all(self):
|
||||||
nova_utils.delete_all_instances()
|
nova_utils.delete_all_instances()
|
||||||
ceilometer_utils.delete_all_ceilometer_alarms()
|
aodh_utils.delete_all_aodh_alarms()
|
||||||
|
|
||||||
def _create_alarm(self, resource_id, alarm_name, unic=False):
|
def _create_alarm(self, resource_id, alarm_name, unic=False):
|
||||||
ceilometer_utils.create_ceilometer_alarm(resource_id=resource_id,
|
aodh_utils.create_aodh_alarm(resource_id=resource_id,
|
||||||
name=alarm_name,
|
name=alarm_name,
|
||||||
unic=unic)
|
unic=unic)
|
||||||
|
|
||||||
|
@ -151,9 +151,9 @@ class TestRca(BaseRcaTest):
|
|||||||
alarm_name=RCA_ALARM_NAME)
|
alarm_name=RCA_ALARM_NAME)
|
||||||
vitrage_alarms = TempestClients.vitrage().alarm.list(
|
vitrage_alarms = TempestClients.vitrage().alarm.list(
|
||||||
vitrage_id=None)
|
vitrage_id=None)
|
||||||
ceilometer_alarms = TempestClients.ceilometer().alarms.list()
|
aodh_alarms = TempestClients.aodh().alarm.list()
|
||||||
|
|
||||||
self._validate_notifier(alarms=ceilometer_alarms,
|
self._validate_notifier(alarms=aodh_alarms,
|
||||||
vitrage_alarms=vitrage_alarms)
|
vitrage_alarms=vitrage_alarms)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
self._handle_exception(e)
|
self._handle_exception(e)
|
||||||
|
55
vitrage_tempest_tests/tests/common/aodh_utils.py
Normal file
55
vitrage_tempest_tests/tests/common/aodh_utils.py
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
# Copyright 2017 - Nokia
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||||
|
# not use this file except in compliance with the License. You may obtain
|
||||||
|
# a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
# License for the specific language governing permissions and limitations
|
||||||
|
# under the License.
|
||||||
|
import random
|
||||||
|
import time
|
||||||
|
from vitrage.datasources.aodh.properties import AodhProperties as AodhProps
|
||||||
|
from vitrage_tempest_tests.tests.common.tempest_clients import TempestClients
|
||||||
|
|
||||||
|
|
||||||
|
def create_aodh_alarm(resource_id=None, name=None, unic=True):
|
||||||
|
if not name:
|
||||||
|
name = '%s-%s' % ('test_', random.randrange(0, 100000, 1))
|
||||||
|
elif unic:
|
||||||
|
name = '%s-%s' % (name, random.randrange(0, 100000, 1))
|
||||||
|
|
||||||
|
aodh_request = _aodh_request(resource_id=resource_id, name=name)
|
||||||
|
TempestClients.aodh().alarm.create(aodh_request)
|
||||||
|
time.sleep(45)
|
||||||
|
|
||||||
|
|
||||||
|
def delete_all_aodh_alarms():
|
||||||
|
alarms = TempestClients.aodh().alarm.list()
|
||||||
|
for alarm in alarms:
|
||||||
|
TempestClients.aodh().alarm.delete(alarm[AodhProps.ALARM_ID])
|
||||||
|
time.sleep(120)
|
||||||
|
|
||||||
|
|
||||||
|
def _aodh_request(resource_id=None, name=None):
|
||||||
|
query = []
|
||||||
|
if resource_id:
|
||||||
|
query = [
|
||||||
|
dict(
|
||||||
|
field=u'traits.resource_id',
|
||||||
|
type='',
|
||||||
|
op=u'eq',
|
||||||
|
value=resource_id)
|
||||||
|
]
|
||||||
|
|
||||||
|
return dict(
|
||||||
|
name=name,
|
||||||
|
description=u'test alarm',
|
||||||
|
event_rule=dict(query=query),
|
||||||
|
severity=u'low',
|
||||||
|
state=u'alarm',
|
||||||
|
type=u'event')
|
@ -29,6 +29,7 @@ class TempestClients(object):
|
|||||||
cls._neutron = None
|
cls._neutron = None
|
||||||
cls._heat = None
|
cls._heat = None
|
||||||
cls._mistral = None
|
cls._mistral = None
|
||||||
|
cls._aodh = None
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def vitrage(cls):
|
def vitrage(cls):
|
||||||
@ -78,3 +79,9 @@ class TempestClients(object):
|
|||||||
if not cls._mistral:
|
if not cls._mistral:
|
||||||
cls._mistral = os_clients.mistral_client(cls._conf)
|
cls._mistral = os_clients.mistral_client(cls._conf)
|
||||||
return cls._mistral
|
return cls._mistral
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def aodh(cls):
|
||||||
|
if not cls._aodh:
|
||||||
|
cls._aodh = os_clients.aodh_client(cls._conf)
|
||||||
|
return cls._aodh
|
||||||
|
@ -16,7 +16,7 @@ from oslo_log import log as logging
|
|||||||
from vitrage_tempest_tests.tests import utils
|
from vitrage_tempest_tests.tests import utils
|
||||||
|
|
||||||
from vitrage_tempest_tests.tests.api.alarms.base import BaseAlarmsTest
|
from vitrage_tempest_tests.tests.api.alarms.base import BaseAlarmsTest
|
||||||
from vitrage_tempest_tests.tests.common import ceilometer_utils
|
from vitrage_tempest_tests.tests.common import aodh_utils
|
||||||
from vitrage_tempest_tests.tests.common import nova_utils
|
from vitrage_tempest_tests.tests.common import nova_utils
|
||||||
from vitrage_tempest_tests.tests.common.tempest_clients import TempestClients
|
from vitrage_tempest_tests.tests.common.tempest_clients import TempestClients
|
||||||
|
|
||||||
@ -36,7 +36,7 @@ class TestAodhAlarm(BaseAlarmsTest):
|
|||||||
try:
|
try:
|
||||||
# Action
|
# Action
|
||||||
nova_utils.create_instances(num_instances=self.NUM_INSTANCE)
|
nova_utils.create_instances(num_instances=self.NUM_INSTANCE)
|
||||||
ceilometer_utils.create_ceilometer_alarm(
|
aodh_utils.create_aodh_alarm(
|
||||||
self._find_instance_resource_id())
|
self._find_instance_resource_id())
|
||||||
|
|
||||||
# Calculate expected results
|
# Calculate expected results
|
||||||
@ -64,14 +64,14 @@ class TestAodhAlarm(BaseAlarmsTest):
|
|||||||
self._handle_exception(e)
|
self._handle_exception(e)
|
||||||
raise
|
raise
|
||||||
finally:
|
finally:
|
||||||
ceilometer_utils.delete_all_ceilometer_alarms()
|
aodh_utils.delete_all_aodh_alarms()
|
||||||
nova_utils.delete_all_instances()
|
nova_utils.delete_all_instances()
|
||||||
|
|
||||||
@utils.tempest_logger
|
@utils.tempest_logger
|
||||||
def test_alarm_without_resource_id(self):
|
def test_alarm_without_resource_id(self):
|
||||||
try:
|
try:
|
||||||
# Action
|
# Action
|
||||||
ceilometer_utils.create_ceilometer_alarm()
|
aodh_utils.create_aodh_alarm()
|
||||||
|
|
||||||
# Calculate expected results
|
# Calculate expected results
|
||||||
api_graph = self.vitrage_client.topology.get(all_tenants=True)
|
api_graph = self.vitrage_client.topology.get(all_tenants=True)
|
||||||
@ -94,7 +94,7 @@ class TestAodhAlarm(BaseAlarmsTest):
|
|||||||
self._handle_exception(e)
|
self._handle_exception(e)
|
||||||
raise
|
raise
|
||||||
finally:
|
finally:
|
||||||
ceilometer_utils.delete_all_ceilometer_alarms()
|
aodh_utils.delete_all_aodh_alarms()
|
||||||
|
|
||||||
def _find_instance_resource_id(self):
|
def _find_instance_resource_id(self):
|
||||||
servers = TempestClients.nova().servers.list()
|
servers = TempestClients.nova().servers.list()
|
||||||
|
Loading…
x
Reference in New Issue
Block a user