diff --git a/requirements.txt b/requirements.txt index 457026058..414bd7ab6 100644 --- a/requirements.txt +++ b/requirements.txt @@ -5,6 +5,7 @@ pbr>=1.6 Babel>=1.3 lxml>=2.3 +python-ceilometerclient>=2.2.1 # Apache-2.0 python-dateutil>=2.4.2 python-novaclient>=2.26.0 networkx>=1.10 diff --git a/test-requirements.txt b/test-requirements.txt index 1bf413034..dff3a335b 100644 --- a/test-requirements.txt +++ b/test-requirements.txt @@ -8,6 +8,7 @@ coverage>=3.6 discover lxml>=2.3 networkx>=1.10 +python-ceilometerclient>=2.2.1 # Apache-2.0 python-novaclient>=2.26.0 python-subunit>=0.0.18 sphinx!=1.2.0,!=1.3b1,<1.3,>=1.1.2 diff --git a/vitrage/common/constants.py b/vitrage/common/constants.py index 66a79c605..428a35154 100644 --- a/vitrage/common/constants.py +++ b/vitrage/common/constants.py @@ -69,6 +69,7 @@ class EntityType(object): STATIC_PHYSICAL = 'static_physical' NAGIOS = 'nagios' VITRAGE = 'vitrage' + AODH = 'aodh' class EventAction(object): diff --git a/vitrage/synchronizer/plugins/__init__.py b/vitrage/synchronizer/plugins/__init__.py index 0cc368073..6f2fdaf8e 100644 --- a/vitrage/synchronizer/plugins/__init__.py +++ b/vitrage/synchronizer/plugins/__init__.py @@ -22,6 +22,7 @@ OPTS = [ 'nova.host', 'nova.instance', 'nova.zone', - 'static_physical'], + 'static_physical', + 'aodh'], help='Names of supported plugins'), ] diff --git a/vitrage/synchronizer/plugins/aodh/__init__.py b/vitrage/synchronizer/plugins/aodh/__init__.py new file mode 100644 index 000000000..74d8785ee --- /dev/null +++ b/vitrage/synchronizer/plugins/aodh/__init__.py @@ -0,0 +1,39 @@ +# 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 + +OPTS = [ + cfg.StrOpt('transformer', + default='vitrage.synchronizer.plugins.aodh.' + 'transformer.AodhTransformer', + help='Aodh plugin transformer class path', + required=True), + cfg.StrOpt('synchronizer', + default='vitrage.synchronizer.plugins.aodh.synchronizer' + '.AodhSynchronizer', + help='Aodh plugin synchronizer class path', + required=True), + cfg.IntOpt('changes_interval', + default=30, + min=30, + help='interval between checking changes in aodh plugin', + required=True), + cfg.StrOpt('user', default='admin', help='Aodh user name'), + cfg.StrOpt('password', default='password', help='Aodh user password'), + cfg.StrOpt('url', default='http://localhost:5000/v2.0/', + help='Aodh authentication url'), + cfg.StrOpt('version', default='2', help='Aodh version'), + cfg.StrOpt('project', default='admin', help='Aodh project'), +] diff --git a/vitrage/synchronizer/plugins/aodh/properties.py b/vitrage/synchronizer/plugins/aodh/properties.py new file mode 100644 index 000000000..bb893945b --- /dev/null +++ b/vitrage/synchronizer/plugins/aodh/properties.py @@ -0,0 +1,38 @@ +# 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 AodhProperties(object): + ALARM_ID = 'alarm_id' + DESCRIPTION = 'description' + ENABLED = 'enabled' + EVENT = 'event' + EVENT_TYPE = 'event_type' + 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' + TIMESTAMP = 'timestamp' + TYPE = 'type' + + +class AodhState(object): + OK = 'ok' + ALARM = 'alarm' + INSUFFICIENT_DATA = 'insufficient_data' diff --git a/vitrage/synchronizer/plugins/aodh/synchronizer.py b/vitrage/synchronizer/plugins/aodh/synchronizer.py new file mode 100644 index 000000000..3356d60af --- /dev/null +++ b/vitrage/synchronizer/plugins/aodh/synchronizer.py @@ -0,0 +1,140 @@ +# 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. +import traceback + +from ceilometerclient import client +from oslo_log import log +from vitrage.common.constants import EntityType +from vitrage.synchronizer.plugins.aodh.properties import AodhProperties \ + as AodhProps +from vitrage.synchronizer.plugins.aodh.properties import AodhState +from vitrage.synchronizer.plugins.base.alarm.synchronizer \ + import BaseAlarmSynchronizer + +LOG = log.getLogger(__name__) + + +class AodhSynchronizer(BaseAlarmSynchronizer): + + def __init__(self, conf): + super(AodhSynchronizer, self).__init__() + + version = conf[EntityType.AODH].version + user = conf[EntityType.AODH].user + password = conf[EntityType.AODH].password + project = conf[EntityType.AODH].project + auth_url = conf[EntityType.AODH].url + + try: + self.client = client.Client(version, + None, + username=user, + password=password, + tenant_name=project, + auth_url=auth_url) + except Exception: + LOG.error("Failed to initialize ceilometer client. Exception: %s", + traceback.print_exc()) + + def _sync_type(self): + return EntityType.AODH + + def _alarm_key(self, alarm): + return alarm[AodhProps.NAME] + + def _get_alarms(self): + return [] + # TODO(iafek): enable the code below + + # try: + # aodh_alarms = self.client.alarms.list() + # return [_convert_alarm(alarm) + # for alarm in aodh_alarms] + # except Exception: + # LOG.error("Exception: %s", traceback.print_exc()) + # return [] + + def _is_erroneous(self, alarm): + return alarm and alarm[AodhProps.STATE] != AodhState.OK + + def _status_changed(self, alarm1, alarm2): + return alarm1 and alarm2 and \ + not alarm1[AodhProps.STATE] == alarm2[AodhProps.STATE] + + def _is_valid(self, alarm): + return True + + @staticmethod + def _convert_event_alarm(alarm): + converted_alarm = AodhSynchronizer._convert_base_alarm(alarm) + event_type, resource_id = \ + AodhSynchronizer._parse_event_rule(alarm.event_rule) + converted_alarm[AodhProps.EVENT_TYPE] = event_type + converted_alarm[AodhProps.RESOURCE_ID] = resource_id + return converted_alarm + + @staticmethod + def _convert_threshold_alarm(alarm): + converted_alarm = AodhSynchronizer._convert_base_alarm(alarm) + converted_alarm[AodhProps.STATE_TIMESTAMP] = alarm.state_timestamp + converted_alarm[AodhProps.RESOURCE_ID] = \ + AodhSynchronizer._parse_threshold_rule(alarm.threshold_rule) + return converted_alarm + + @staticmethod + def _convert_base_alarm(alarm): + # TODO(iafek): what if the alarm state is 'insufficient data' + + return { + 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.SEVERITY: alarm.severity, + AodhProps.STATE: alarm.state, + AodhProps.TIMESTAMP: alarm.timestamp, + AodhProps.TYPE: alarm.type + } + + @staticmethod + def _parse_event_rule(rule): + event_type = rule[AodhProps.EVENT_TYPE] + resource_id = \ + AodhSynchronizer._parse_resource_id(rule[AodhProps.QUERY]) + return event_type, resource_id + + @staticmethod + def _parse_threshold_rule(rule): + return AodhSynchronizer._parse_resource_id(rule[AodhProps.QUERY]) + + @staticmethod + def _parse_resource_id(query_fields): + for query in query_fields: + field = query['field'] + if field == AodhProps.RESOURCE_ID: + return query['value'] + else: + return None + + +def _convert_alarm(alarm): + alarm_type = alarm.type + if alarm_type == AodhProps.EVENT: + return AodhSynchronizer._convert_event_alarm(alarm) + elif alarm_type == AodhProps.THRESHOLD: + return AodhSynchronizer._convert_threshold_alarm(alarm) + else: + LOG.info('Unsopported Aodh alarm of type %s' % alarm_type) diff --git a/vitrage/synchronizer/plugins/aodh/transformer.py b/vitrage/synchronizer/plugins/aodh/transformer.py new file mode 100644 index 000000000..5000b0bd8 --- /dev/null +++ b/vitrage/synchronizer/plugins/aodh/transformer.py @@ -0,0 +1,87 @@ +# Copyright 2016 - Nokia +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +from oslo_log import log as logging + +from vitrage.common.constants import EntityCategory +from vitrage.common.constants import SynchronizerProperties as SyncProps +from vitrage.common.constants import VertexProperties as VProps +from vitrage.common import datetime_utils +import vitrage.graph.utils as graph_utils +from vitrage.synchronizer.plugins.aodh.properties import AodhProperties \ + as AodhProps +from vitrage.synchronizer.plugins.base.alarm.properties \ + import AlarmProperties as AlarmProps +from vitrage.synchronizer.plugins.base.alarm.transformer \ + import BaseAlarmTransformer +from vitrage.synchronizer.plugins import transformer_base as tbase + +LOG = logging.getLogger(__name__) + + +class AodhTransformer(BaseAlarmTransformer): + + STATUS_OK = 'ok' + + def __init__(self, transformers): + super(AodhTransformer, self).__init__(transformers) + + def _create_entity_vertex(self, entity_event): + metadata = { + VProps.NAME: entity_event[AodhProps.NAME], + VProps.SEVERITY: entity_event[AodhProps.SEVERITY], + AodhProps.DESCRIPTION: entity_event[AodhProps.DESCRIPTION], + AodhProps.ENABLED: entity_event[AodhProps.ENABLED], + VProps.PROJECT_ID: entity_event[AodhProps.PROJECT_ID], + AodhProps.REPEAT_ACTIONS: entity_event[AodhProps.REPEAT_ACTIONS], + 'alarm_type': entity_event[AodhProps.TYPE] + } + + if entity_event[AodhProps.TYPE] == AodhProps.EVENT: + metadata[AodhProps.EVENT_TYPE] = entity_event[AodhProps.EVENT_TYPE] + + elif entity_event[AodhProps.TYPE] == AodhProps.THRESHOLD: + metadata[AodhProps.STATE_TIMESTAMP] = \ + entity_event[AodhProps.STATE_TIMESTAMP] + + return graph_utils.create_vertex( + self.extract_key(entity_event), + entity_id=entity_event[AodhProps.ALARM_ID], + entity_category=EntityCategory.ALARM, + entity_type=entity_event[SyncProps.SYNC_TYPE], + entity_state=AlarmProps.ALARM_STATE, + update_timestamp=AodhTransformer._timestamp(entity_event), + metadata=metadata) + + def _create_neighbors(self, entity_event): + # TODO(iafek): get neighbour resource by its id + return [] + + def _ok_status(self, entity_event): + return entity_event[AodhProps.STATE] == self.STATUS_OK + + def extract_key(self, entity_event): + sync_type = entity_event[SyncProps.SYNC_TYPE] + alarm_name = entity_event[AodhProps.NAME] + resource_id = entity_event[AodhProps.RESOURCE_ID] + return (tbase.build_key(self.key_values([sync_type, + resource_id, + alarm_name])) if resource_id + else tbase.build_key(self.key_values([sync_type, alarm_name]))) + + @staticmethod + def _timestamp(entity_event): + return datetime_utils.change_time_str_format( + entity_event[AodhProps.TIMESTAMP], + '%Y-%m-%dT%H:%M:%S.%f', + tbase.TIMESTAMP_FORMAT) diff --git a/vitrage/synchronizer/plugins/base/__init__.py b/vitrage/synchronizer/plugins/base/__init__.py new file mode 100644 index 000000000..dd32b852f --- /dev/null +++ b/vitrage/synchronizer/plugins/base/__init__.py @@ -0,0 +1,15 @@ +# Copyright 2016 - Nokia +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +__author__ = 'stack' diff --git a/vitrage/synchronizer/plugins/base/alarm/__init__.py b/vitrage/synchronizer/plugins/base/alarm/__init__.py new file mode 100644 index 000000000..dd32b852f --- /dev/null +++ b/vitrage/synchronizer/plugins/base/alarm/__init__.py @@ -0,0 +1,15 @@ +# Copyright 2016 - Nokia +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +__author__ = 'stack' diff --git a/vitrage/synchronizer/plugins/base/alarm/properties.py b/vitrage/synchronizer/plugins/base/alarm/properties.py new file mode 100644 index 000000000..f0f4a5372 --- /dev/null +++ b/vitrage/synchronizer/plugins/base/alarm/properties.py @@ -0,0 +1,18 @@ +# 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. +__author__ = 'stack' + + +class AlarmProperties(object): + ALARM_STATE = 'Active' diff --git a/vitrage/synchronizer/plugins/base/alarm/synchronizer.py b/vitrage/synchronizer/plugins/base/alarm/synchronizer.py new file mode 100644 index 000000000..e8cffbb57 --- /dev/null +++ b/vitrage/synchronizer/plugins/base/alarm/synchronizer.py @@ -0,0 +1,122 @@ +# Copyright 2016 - Nokia +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +from oslo_log import log +from vitrage.synchronizer.plugins.synchronizer_base import SynchronizerBase + +LOG = log.getLogger(__name__) + + +class BaseAlarmSynchronizer(SynchronizerBase): + def __init__(self): + super(SynchronizerBase, self).__init__() + self.cache = dict() + + def _sync_type(self): + """Return the type of the plugin """ + pass + + def _alarm_key(self, alarm): + """Return a unique key of the alarm, to identify it in the cache """ + pass + + def _get_alarms(self): + """Return the list of alarms of this plugin """ + pass + + def _enrich_alarms(self, alarms): + """Optionally add more data to the alarms + + :param alarms: list of alarms to be enriched + :return: + """ + pass + + def _is_erroneous(self, alarm): + """Check if the state of the alarm is erroneous + + :param alarm: + :return: True/False based on the alarm state + """ + pass + + def _status_changed(self, alarm1, alarm2): + """Check if the status of the two alarms is different + + :param alarm1: + :param alarm2: + :return: True/False based on the alarms states + """ + pass + + def _is_valid(self, alarm): + """Check if the alarm is valid + + :param alarm: an alarm to check + :return: True/False + """ + pass + + def get_all(self, sync_mode): + return self.make_pickleable(self._get_all_alarms(), + self._sync_type(), + sync_mode) + + def get_changes(self, sync_mode): + return self.make_pickleable(self._get_changed_alarms(), + self._sync_type(), + sync_mode) + + def _get_all_alarms(self): + alarms = self._get_alarms() + self._enrich_alarms(alarms) + return self._filter_and_cache_alarms( + alarms, + BaseAlarmSynchronizer._filter_get_all) + + def _get_changed_alarms(self): + alarms = self._get_alarms() + self._enrich_alarms(alarms) + return self._filter_and_cache_alarms( + alarms, + BaseAlarmSynchronizer._filter_get_changes) + + def _filter_and_cache_alarms(self, alarms, filter_): + alarms_to_update = [] + + for alarm in alarms: + alarm_key = self._alarm_key(alarm) + old_alarm = self.cache.get(alarm_key, None) + + if filter_(self, alarm, old_alarm): + alarms_to_update.append(alarm) + + self.cache[alarm_key] = alarm + + return alarms_to_update + + def _filter_get_all(self, alarm, old_alarm): + return alarm \ + if self._is_valid(alarm) and \ + (self._is_erroneous(alarm) or self._is_erroneous(old_alarm)) \ + else None + + def _filter_get_changes(self, alarm, old_alarm): + if not self._is_valid(alarm): + return None + if self._status_changed(alarm, old_alarm): + return alarm + elif not old_alarm and self._is_erroneous(alarm): + return alarm + else: + return None diff --git a/vitrage/synchronizer/plugins/base/alarm/transformer.py b/vitrage/synchronizer/plugins/base/alarm/transformer.py new file mode 100644 index 000000000..a8c541a3e --- /dev/null +++ b/vitrage/synchronizer/plugins/base/alarm/transformer.py @@ -0,0 +1,50 @@ +# Copyright 2016 - Nokia +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +from oslo_log import log as logging + +from vitrage.common.constants import EntityCategory +from vitrage.common.constants import EventAction +from vitrage.common.constants import SynchronizerProperties as SyncProps +from vitrage.common.constants import SyncMode +from vitrage.common.exception import VitrageTransformerError +from vitrage.synchronizer.plugins import transformer_base as tbase + +LOG = logging.getLogger(__name__) + + +class BaseAlarmTransformer(tbase.TransformerBase): + + def __init__(self, transformers): + self.transformers = transformers + + def _ok_status(self, entity_event): + pass + + def create_placeholder_vertex(self, properties={}): + LOG.info('An alarm cannot be a placeholder') + pass + + def _extract_action_type(self, entity_event): + sync_mode = entity_event[SyncProps.SYNC_MODE] + if sync_mode in (SyncMode.UPDATE, SyncMode.SNAPSHOT): + if self._ok_status(entity_event): + return EventAction.DELETE_ENTITY + else: + return EventAction.UPDATE_ENTITY + if SyncMode.INIT_SNAPSHOT == sync_mode: + return EventAction.CREATE_ENTITY + raise VitrageTransformerError('Invalid sync mode: (%s)' % sync_mode) + + def key_values(self, mutable_fields=[]): + return [EntityCategory.ALARM] + mutable_fields diff --git a/vitrage/synchronizer/plugins/nagios/properties.py b/vitrage/synchronizer/plugins/nagios/properties.py index f7accd702..0e9da6a74 100644 --- a/vitrage/synchronizer/plugins/nagios/properties.py +++ b/vitrage/synchronizer/plugins/nagios/properties.py @@ -13,7 +13,6 @@ # under the License. -# TODO(ifat_afek): unite with nagios transformer properties class NagiosProperties(object): NUM_COLUMNS = 7 RESOURCE_TYPE = 'resource_type' diff --git a/vitrage/synchronizer/plugins/nagios/synchronizer.py b/vitrage/synchronizer/plugins/nagios/synchronizer.py index d0fd0f98e..c3f21c50b 100644 --- a/vitrage/synchronizer/plugins/nagios/synchronizer.py +++ b/vitrage/synchronizer/plugins/nagios/synchronizer.py @@ -21,50 +21,33 @@ from vitrage.common.constants import EntityType from vitrage.common.constants import SynchronizerProperties as SyncProps from vitrage.i18n import _LE from vitrage.i18n import _LW +from vitrage.synchronizer.plugins.base.alarm.synchronizer \ + import BaseAlarmSynchronizer from vitrage.synchronizer.plugins.nagios.config import NagiosConfig from vitrage.synchronizer.plugins.nagios.parser import NagiosParser from vitrage.synchronizer.plugins.nagios.properties import NagiosProperties \ as NagiosProps from vitrage.synchronizer.plugins.nagios.properties import NagiosStatus -from vitrage.synchronizer.plugins.synchronizer_base import SynchronizerBase LOG = log.getLogger(__name__) -class NagiosSynchronizer(SynchronizerBase): +class NagiosSynchronizer(BaseAlarmSynchronizer): ServiceKey = namedtuple('ServiceKey', ['host_name', 'service']) def __init__(self, conf): super(NagiosSynchronizer, self).__init__() self.conf = conf - self.cache = dict() self.config = NagiosConfig(conf) - def get_all(self, sync_mode): - return self.make_pickleable(self._get_all_services(), - EntityType.NAGIOS, - sync_mode) + def _sync_type(self): + return EntityType.NAGIOS - def get_changes(self, sync_mode): - return self.make_pickleable(self._get_changed_services(), - EntityType.NAGIOS, - sync_mode) + def _alarm_key(self, alarm): + return self.ServiceKey(host_name=alarm[NagiosProps.RESOURCE_NAME], + service=alarm[NagiosProps.SERVICE]) - def _get_all_services(self): - nagios_services = self._get_services_from_nagios() - self._enrich_services(nagios_services) - return self._filter_and_cache_services( - nagios_services, - NagiosSynchronizer._filter_get_all) - - def _get_changed_services(self): - nagios_services = self._get_services_from_nagios() - self._enrich_services(nagios_services) - return self._filter_and_cache_services( - nagios_services, - NagiosSynchronizer._filter_get_changes) - - def _get_services_from_nagios(self): + def _get_alarms(self): nagios_user = self.conf.nagios.user nagios_password = self.conf.nagios.password nagios_url = self.conf.nagios.url @@ -95,59 +78,27 @@ class NagiosSynchronizer(SynchronizerBase): response.status_code) return [] - def _enrich_services(self, nagios_services): - for service in nagios_services: + def _enrich_alarms(self, alarms): + for alarm in alarms: # based on nagios configuration file, convert nagios host name # to vitrage resource type and name - service[SyncProps.SYNC_TYPE] = NagiosProps.NAGIOS + alarm[SyncProps.SYNC_TYPE] = NagiosProps.NAGIOS - nagios_host = service[NagiosProps.RESOURCE_NAME] + nagios_host = alarm[NagiosProps.RESOURCE_NAME] vitrage_resource = self.config.get_vitrage_resource(nagios_host) - service[NagiosProps.RESOURCE_TYPE] = \ + alarm[NagiosProps.RESOURCE_TYPE] = \ vitrage_resource[0] if vitrage_resource else None - service[NagiosProps.RESOURCE_NAME] = \ - vitrage_resource[1] if vitrage_resource \ - else service[NagiosProps.RESOURCE_NAME] + alarm[NagiosProps.RESOURCE_NAME] = \ + vitrage_resource[1] if vitrage_resource else None - def _filter_and_cache_services(self, nagios_services, filter_): - services_to_update = [] + def _is_erroneous(self, alarm): + return alarm and alarm[NagiosProps.STATUS] != NagiosStatus.OK - for service in nagios_services: - service_key = self.ServiceKey( - host_name=service[NagiosProps.RESOURCE_NAME], - service=service[NagiosProps.SERVICE]) + def _status_changed(self, alarm1, alarm2): + return alarm1 and alarm2 and \ + not alarm1[NagiosProps.STATUS] == alarm2[NagiosProps.STATUS] - old_service = self.cache.get(service_key, None) - - if filter_(service, old_service): - services_to_update.append(service) - - self.cache[service_key] = service - - return services_to_update - - @staticmethod - def _filter_get_all(service, old_service): - return service \ - if (NagiosSynchronizer._is_erroneous(service) or - NagiosSynchronizer._is_erroneous(old_service)) \ - else None - - @staticmethod - def _filter_get_changes(service, old_service): - if NagiosSynchronizer._status_changed(service, old_service): - return service - elif not old_service and NagiosSynchronizer._is_erroneous(service): - return service - else: - return None - - @staticmethod - def _is_erroneous(service): - return service and service[NagiosProps.STATUS] != NagiosStatus.OK - - @staticmethod - def _status_changed(service1, service2): - return service1 and service2 and \ - not service1[NagiosProps.STATUS] == service2[NagiosProps.STATUS] + def _is_valid(self, alarm): + return alarm[NagiosProps.RESOURCE_TYPE] is not None and \ + alarm[NagiosProps.RESOURCE_NAME] is not None diff --git a/vitrage/synchronizer/plugins/nagios/transformer.py b/vitrage/synchronizer/plugins/nagios/transformer.py index 138b35d6e..268f9bf56 100644 --- a/vitrage/synchronizer/plugins/nagios/transformer.py +++ b/vitrage/synchronizer/plugins/nagios/transformer.py @@ -16,29 +16,26 @@ from oslo_log import log as logging from vitrage.common.constants import EdgeLabels from vitrage.common.constants import EntityCategory from vitrage.common.constants import EntityType -from vitrage.common.constants import EventAction from vitrage.common.constants import SynchronizerProperties as SyncProps -from vitrage.common.constants import SyncMode from vitrage.common.constants import VertexProperties as VProps from vitrage.common import datetime_utils -from vitrage.common.exception import VitrageTransformerError import vitrage.graph.utils as graph_utils +from vitrage.synchronizer.plugins.base.alarm.properties \ + import AlarmProperties as AlarmProps +from vitrage.synchronizer.plugins.base.alarm.transformer \ + import BaseAlarmTransformer from vitrage.synchronizer.plugins.nagios.properties import NagiosProperties from vitrage.synchronizer.plugins import transformer_base as tbase LOG = logging.getLogger(__name__) -class NagiosTransformer(tbase.TransformerBase): +class NagiosTransformer(BaseAlarmTransformer): STATUS_OK = 'OK' - NAGIOS_ALARM_STATE = 'Active' def __init__(self, transformers): - self.transformers = transformers - - def create_placeholder_vertex(self, properties={}): - LOG.info('Nagios alarm cannot be a placeholder') + super(NagiosTransformer, self).__init__(transformers) def _create_entity_vertex(self, entity_event): @@ -57,7 +54,7 @@ class NagiosTransformer(tbase.TransformerBase): self.extract_key(entity_event), entity_category=EntityCategory.ALARM, entity_type=entity_event[SyncProps.SYNC_TYPE], - entity_state=self.NAGIOS_ALARM_STATE, + entity_state=AlarmProps.ALARM_STATE, update_timestamp=timestamp, metadata=metadata) @@ -100,16 +97,8 @@ class NagiosTransformer(tbase.TransformerBase): LOG.warning('Cannot transform host, host transformer does not exist') return None - def _extract_action_type(self, entity_event): - sync_mode = entity_event[SyncProps.SYNC_MODE] - if sync_mode in (SyncMode.UPDATE, SyncMode.SNAPSHOT): - if entity_event[NagiosProperties.STATUS] == self.STATUS_OK: - return EventAction.DELETE_ENTITY - else: - return EventAction.UPDATE_ENTITY - if SyncMode.INIT_SNAPSHOT == sync_mode: - return EventAction.CREATE_ENTITY - raise VitrageTransformerError('Invalid sync mode: (%s)' % sync_mode) + def _ok_status(self, entity_event): + return entity_event[NagiosProperties.STATUS] == self.STATUS_OK def extract_key(self, entity_event): @@ -119,6 +108,3 @@ class NagiosTransformer(tbase.TransformerBase): return tbase.build_key(self.key_values([sync_type, resource_name, alarm_name])) - - def key_values(self, mutable_fields=[]): - return [EntityCategory.ALARM] + mutable_fields diff --git a/vitrage/tests/unit/synchronizer/nagios/synchronizer_with_mock_data.py b/vitrage/tests/unit/synchronizer/nagios/synchronizer_with_mock_data.py index 0fa06b0aa..3fe35c935 100644 --- a/vitrage/tests/unit/synchronizer/nagios/synchronizer_with_mock_data.py +++ b/vitrage/tests/unit/synchronizer/nagios/synchronizer_with_mock_data.py @@ -30,7 +30,7 @@ class NagiosSynchronizerWithMockData(NagiosSynchronizer): def set_service_datas(self, service_datas): self.service_datas = service_datas - def _get_services_from_nagios(self): + def _get_alarms(self): alarms = [] for service_data in self.service_datas: generators = mock_sync.simple_nagios_alarm_generators( diff --git a/vitrage/tests/unit/synchronizer/nagios/test_nagios_alarm_transformer.py b/vitrage/tests/unit/synchronizer/nagios/test_nagios_alarm_transformer.py index 6f35d9480..ff48d03a4 100644 --- a/vitrage/tests/unit/synchronizer/nagios/test_nagios_alarm_transformer.py +++ b/vitrage/tests/unit/synchronizer/nagios/test_nagios_alarm_transformer.py @@ -20,6 +20,8 @@ from vitrage.common.constants import EventAction from vitrage.common.constants import SynchronizerProperties as SyncProps from vitrage.common.constants import SyncMode from vitrage.common.constants import VertexProperties as VProps +from vitrage.synchronizer.plugins.base.alarm.properties \ + import AlarmProperties as AlarmProps from vitrage.synchronizer.plugins.nagios.properties import NagiosProperties from vitrage.synchronizer.plugins.nagios.transformer import NagiosTransformer from vitrage.synchronizer.plugins.nova.host.transformer import HostTransformer @@ -102,8 +104,7 @@ class NagiosTransformerTest(base.BaseTest): self.assertEqual(EntityCategory.ALARM, vertex[VProps.CATEGORY]) self.assertEqual(event[SyncProps.SYNC_TYPE], vertex[VProps.TYPE]) self.assertEqual(event[NagiosProperties.SERVICE], vertex[VProps.NAME]) - self.assertEqual(NagiosTransformer.NAGIOS_ALARM_STATE, - vertex[VProps.STATE]) + self.assertEqual(AlarmProps.ALARM_STATE, vertex[VProps.STATE]) self.assertEqual(event[NagiosProperties.STATUS], vertex[VProps.SEVERITY]) diff --git a/vitrage/tests/unit/synchronizer/nagios/test_nagios_synchronizer.py b/vitrage/tests/unit/synchronizer/nagios/test_nagios_synchronizer.py index 374a9f805..558398659 100644 --- a/vitrage/tests/unit/synchronizer/nagios/test_nagios_synchronizer.py +++ b/vitrage/tests/unit/synchronizer/nagios/test_nagios_synchronizer.py @@ -28,11 +28,11 @@ LOG = logging.getLogger(__name__) class NagiosSynchronizerTest(NagiosBaseTest): OPTS = [ - cfg.StrOpt( - 'config_file', - default=utils.get_resources_dir() + '/nagios/nagios_conf.yaml', - help='Nagios configuation file' - ), + cfg.StrOpt('config_file', + default=utils.get_resources_dir() + + '/nagios/nagios_conf.yaml', + help='Nagios configuation file' + ), ] @classmethod @@ -65,7 +65,7 @@ class NagiosSynchronizerTest(NagiosBaseTest): service_data2, service_data3]) - services = nagios_synchronizer._get_all_services() + services = nagios_synchronizer._get_all_alarms() # Test assertions # Services with status OK should not be returned @@ -87,7 +87,7 @@ class NagiosSynchronizerTest(NagiosBaseTest): service_data2, service_data3]) - services = nagios_synchronizer._get_all_services() + services = nagios_synchronizer._get_all_alarms() # Test assertions self.assertIsNotNone(services, 'No services returned') @@ -109,7 +109,7 @@ class NagiosSynchronizerTest(NagiosBaseTest): service_data2, service_data3]) - services = nagios_synchronizer._get_all_services() + services = nagios_synchronizer._get_all_alarms() # Test assertions self.assertIsNotNone(services, 'No services returned') @@ -132,7 +132,7 @@ class NagiosSynchronizerTest(NagiosBaseTest): service_data2, service_data3]) - services = nagios_synchronizer._get_all_services() + services = nagios_synchronizer._get_all_alarms() # Test assertions # The services of service_data1/2 should be returned although their @@ -143,7 +143,7 @@ class NagiosSynchronizerTest(NagiosBaseTest): self._assert_contains(service_data2, services) # Action - services = nagios_synchronizer._get_all_services() + services = nagios_synchronizer._get_all_alarms() # Test assertions # Calling get_services again should not return anything, since all @@ -176,7 +176,7 @@ class NagiosSynchronizerTest(NagiosBaseTest): service_data2, service_data3]) - services = nagios_synchronizer._get_changed_services() + services = nagios_synchronizer._get_changed_alarms() # Test assertions # Services with status OK should not be returned @@ -198,7 +198,7 @@ class NagiosSynchronizerTest(NagiosBaseTest): service_data2, service_data3]) - services = nagios_synchronizer._get_changed_services() + services = nagios_synchronizer._get_changed_alarms() # Test assertions self.assertIsNotNone(services, 'No services returned') @@ -220,7 +220,7 @@ class NagiosSynchronizerTest(NagiosBaseTest): service_data2, service_data3]) - services = nagios_synchronizer._get_changed_services() + services = nagios_synchronizer._get_changed_alarms() # Test assertions self.assertIsNotNone(services, 'No services returned') @@ -243,7 +243,7 @@ class NagiosSynchronizerTest(NagiosBaseTest): service_data2, service_data3]) - services = nagios_synchronizer._get_changed_services() + services = nagios_synchronizer._get_changed_alarms() # Test assertions self.assertIsNotNone(services, 'No services returned') @@ -265,7 +265,7 @@ class NagiosSynchronizerTest(NagiosBaseTest): service_data2, service_data3]) - services = nagios_synchronizer._get_changed_services() + services = nagios_synchronizer._get_changed_alarms() # Test assertions self.assertIsNotNone(services, 'No services returned') @@ -274,7 +274,7 @@ class NagiosSynchronizerTest(NagiosBaseTest): self._assert_contains(service_data2, services) # Action - services = nagios_synchronizer._get_changed_services() + services = nagios_synchronizer._get_changed_alarms() # Test assertions self.assertIsNotNone(services, 'services is None') @@ -301,7 +301,7 @@ class NagiosSynchronizerTest(NagiosBaseTest): service_data2, service_data3]) - services = nagios_synchronizer._get_changed_services() + services = nagios_synchronizer._get_changed_alarms() # Test assertions self.assertIsNotNone(services, 'No services returned') @@ -309,7 +309,7 @@ class NagiosSynchronizerTest(NagiosBaseTest): self._assert_contains(service_data1, services) # Action - services = nagios_synchronizer._get_changed_services() + services = nagios_synchronizer._get_changed_alarms() # Test assertions # Calling get_changes for the second time should return nothing @@ -317,7 +317,7 @@ class NagiosSynchronizerTest(NagiosBaseTest): self.assertEqual(0, len(services)) # Action - services = nagios_synchronizer._get_all_services() + services = nagios_synchronizer._get_all_alarms() # Test assertions self.assertIsNotNone(services, 'No services returned') @@ -325,7 +325,7 @@ class NagiosSynchronizerTest(NagiosBaseTest): self._assert_contains(service_data1, services) # Action - services = nagios_synchronizer._get_all_services() + services = nagios_synchronizer._get_all_alarms() # Test assertions # Calling get_all for the second time should return the same results @@ -348,7 +348,7 @@ class NagiosSynchronizerTest(NagiosBaseTest): service_data2, service_data3]) - services = nagios_synchronizer._get_all_services() + services = nagios_synchronizer._get_all_alarms() # Test assertions self.assertIsNotNone(services, 'No services returned') @@ -357,7 +357,7 @@ class NagiosSynchronizerTest(NagiosBaseTest): self._assert_contains(service_data2, services) # Action - services = nagios_synchronizer._get_changed_services() + services = nagios_synchronizer._get_changed_alarms() # Test assertions # Calling get_changes after get_all should return nothing @@ -365,7 +365,7 @@ class NagiosSynchronizerTest(NagiosBaseTest): self.assertEqual(0, len(services)) # Action - services = nagios_synchronizer._get_all_services() + services = nagios_synchronizer._get_all_alarms() # Test assertions # Calling get_all for the second time should return the same results @@ -389,7 +389,7 @@ class NagiosSynchronizerTest(NagiosBaseTest): service_data2, service_data3]) - services = nagios_synchronizer._get_changed_services() + services = nagios_synchronizer._get_changed_alarms() # Test assertions self.assertIsNotNone(services, 'No services returned') @@ -412,7 +412,7 @@ class NagiosSynchronizerTest(NagiosBaseTest): service_data2, service_data3]) - services = nagios_synchronizer._get_changed_services() + services = nagios_synchronizer._get_changed_alarms() # Test assertions self.assertIsNotNone(services, 'No services returned') @@ -420,14 +420,14 @@ class NagiosSynchronizerTest(NagiosBaseTest): self._assert_contains(service_data1, services) # Action - services = nagios_synchronizer._get_changed_services() + services = nagios_synchronizer._get_changed_alarms() # Test assertions self.assertIsNotNone(services, 'services is None') self.assertEqual(0, len(services)) # Action - services = nagios_synchronizer._get_all_services() + services = nagios_synchronizer._get_all_alarms() # Test assertions # Calling get_all for the second time should return the same results