From ede475c7d5c7ba03037a01d6e25ca3052d00253f Mon Sep 17 00:00:00 2001 From: xupeipei Date: Wed, 10 Jan 2018 09:02:10 +0800 Subject: [PATCH] Add codes of sending parsed snmp trap to datasource since there is no snmp triggered datasource, codes of alarm datasource processing snmp event and config file are added in test case. Change-Id: I9a4f0c8405f52bf9269b0056d26b31fdafdad0aa Implements: blueprint snmp-support Signed-off-by: xupeipei --- devstack/plugin.sh | 3 +- devstack/settings | 2 + vitrage/snmp_parsing/__init__.py | 4 +- vitrage/snmp_parsing/properties.py | 19 +++++ vitrage/snmp_parsing/service.py | 72 +++++++++++++++++-- .../snmp_parsing/snmp_parsing_conf.yaml | 3 + vitrage/tests/unit/snmp_parsing/__init__.py | 15 ++++ .../unit/snmp_parsing/test_snmp_parsing.py | 17 ++++- 8 files changed, 124 insertions(+), 11 deletions(-) create mode 100644 vitrage/snmp_parsing/properties.py create mode 100644 vitrage/tests/resources/snmp_parsing/snmp_parsing_conf.yaml diff --git a/devstack/plugin.sh b/devstack/plugin.sh index f27904264..6ca60f59a 100644 --- a/devstack/plugin.sh +++ b/devstack/plugin.sh @@ -306,6 +306,7 @@ function start_vitrage { run_process vitrage-notifier "$VITRAGE_BIN_DIR/vitrage-notifier --config-file $VITRAGE_CONF" run_process vitrage-ml "$VITRAGE_BIN_DIR/vitrage-ml --config-file $VITRAGE_CONF" run_process vitrage-persistor "$VITRAGE_BIN_DIR/vitrage-persistor --config-file $VITRAGE_CONF" + run_process vitrage-snmp-parsing "$VITRAGE_BIN_DIR/vitrage-snmp-parsing --config-file $VITRAGE_CONF" write_systemd_dependency vitrage-graph vitrage-collector @@ -335,7 +336,7 @@ function stop_vitrage { disable_apache_site vitrage restart_apache_server fi - for serv in vitrage-api vitrage-collector vitrage-graph vitrage-notifier vitrage-persistor; do + for serv in vitrage-api vitrage-collector vitrage-graph vitrage-notifier vitrage-persistor vitrage-snmp-parsing; do stop_process $serv done } diff --git a/devstack/settings b/devstack/settings index 2a997d23b..a5a0ebad8 100644 --- a/devstack/settings +++ b/devstack/settings @@ -11,6 +11,8 @@ enable_service vitrage-collector enable_service vitrage-ml # Persistor enable_service vitrage-persistor +# snmp_parsing +enable_service vitrage-snmp-parsing # Default directories VITRAGE_DIR=$DEST/vitrage diff --git a/vitrage/snmp_parsing/__init__.py b/vitrage/snmp_parsing/__init__.py index 134850ce0..5d1731549 100644 --- a/vitrage/snmp_parsing/__init__.py +++ b/vitrage/snmp_parsing/__init__.py @@ -19,6 +19,6 @@ OPTS = [ default=8162, help='The listening port of snmp_parsing service'), cfg.StrOpt('oid_mapping', - default='/etc/vitrage/snmp_parsing_conf.yaml', - help='The default path of oid_mapping yaml file'), + default='', + help='The default path of oid_mapping yaml file.'), ] diff --git a/vitrage/snmp_parsing/properties.py b/vitrage/snmp_parsing/properties.py new file mode 100644 index 000000000..99cff7a18 --- /dev/null +++ b/vitrage/snmp_parsing/properties.py @@ -0,0 +1,19 @@ +# Copyright 2018 - 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. + + +class SnmpEventProperties(object): + SYSTEM_OID = 'system_oid' + SYSTEM = 'system' + DATASOURCE = 'datasource' diff --git a/vitrage/snmp_parsing/service.py b/vitrage/snmp_parsing/service.py index e07901578..b5e5be2ed 100644 --- a/vitrage/snmp_parsing/service.py +++ b/vitrage/snmp_parsing/service.py @@ -12,27 +12,37 @@ # License for the specific language governing permissions and limitations # under the License. +from datetime import datetime +import json +from oslo_log import log +import oslo_messaging +from oslo_service import service as os_service +from oslo_utils import uuidutils from pyasn1.codec.ber import decoder from pysnmp.carrier.asyncore.dgram import udp from pysnmp.carrier.asyncore.dgram import udp6 from pysnmp.carrier.asyncore.dispatch import AsyncoreDispatcher from pysnmp.proto import api as snmp_api from pysnmp.proto.rfc1902 import Integer - -from oslo_log import log -from oslo_service import service as os_service import sys +from vitrage.common.constants import EventProperties +from vitrage.datasources.transformer_base import extract_field_value +from vitrage.messaging import get_transport +from vitrage.snmp_parsing.properties import SnmpEventProperties as SEProps +from vitrage.utils.file import load_yaml_file + LOG = log.getLogger(__name__) class SnmpParsingService(os_service.Service): - RUN_FORVER = 1 + RUN_FOREVER = 1 def __init__(self, conf): super(SnmpParsingService, self).__init__() self.conf = conf self.listening_port = conf.snmp_parsing.snmp_listening_port + self._init_oslo_notifier() def start(self): LOG.info("Vitrage SNMP Parsing Service - Starting...") @@ -53,7 +63,7 @@ class SnmpParsingService(os_service.Service): transport_dispatcher.registerTransport(udp6.domainName, udp6_transport) LOG.info("Vitrage SNMP Parsing Service - Started!") - transport_dispatcher.jobStarted(self.RUN_FORVER) + transport_dispatcher.jobStarted(self.RUN_FOREVER) try: transport_dispatcher.runDispatcher() except Exception: @@ -88,8 +98,8 @@ class SnmpParsingService(os_service.Service): else p_mod.apiPDU.getVarBinds(req_pdu) binds_dict = self._convert_binds_to_dict(ver_binds) - LOG.debug('Received binds info after convert: %s' % binds_dict) - # TODO(peipei): need to send to message queue + LOG.debug('Receive binds info after convert: %s' % binds_dict) + self._send_snmp_to_queue(binds_dict) def _convert_binds_to_dict(self, var_binds): binds_dict = {} @@ -104,3 +114,51 @@ class SnmpParsingService(os_service.Service): if sys.version_info[0] < 3: return str(val).decode('iso-8859-1') return str(val) + + def _init_oslo_notifier(self): + self.oslo_notifier = None + try: + self.publisher = 'vitrage-snmp-parsing' + self.oslo_notifier = oslo_messaging.Notifier( + get_transport(self.conf), + driver='messagingv2', + publisher_id=self.publisher, + topics=['vitrage_notifications']) + except Exception as e: + LOG.warning('Failed to initialize oslo notifier %s', str(e)) + + def _send_snmp_to_queue(self, snmp_trap): + if str == type(snmp_trap): + snmp_trap = json.loads(snmp_trap) + try: + event_type = self._get_event_type(snmp_trap) + if not event_type: + return + event = {EventProperties.TIME: datetime.utcnow(), + EventProperties.TYPE: event_type, + EventProperties.DETAILS: snmp_trap} + LOG.debug('snmp oslo_notifier event: %s' % event) + self.oslo_notifier.info( + ctxt={'message_id': uuidutils.generate_uuid(), + 'publisher_id': self.publisher, + 'timestamp': datetime.utcnow()}, + event_type=event_type, + payload=event) + except Exception as e: + LOG.warning('Snmp failed to post event. Exception: %s', e) + + def _get_event_type(self, snmp_trap): + yaml_file_content = load_yaml_file(self.conf.snmp_parsing.oid_mapping) + if not yaml_file_content: + LOG.warning('No snmp trap is configured!') + return None + + for mapping_info in yaml_file_content: + system_oid = extract_field_value(mapping_info, SEProps.SYSTEM_OID) + conf_system = extract_field_value(mapping_info, SEProps.SYSTEM) + if conf_system == extract_field_value(snmp_trap, system_oid): + LOG.debug('snmp trap mapped the system: %s.' % conf_system) + return extract_field_value(mapping_info, SEProps.DATASOURCE) + + LOG.error("Snmp trap does not contain system info!") + return None diff --git a/vitrage/tests/resources/snmp_parsing/snmp_parsing_conf.yaml b/vitrage/tests/resources/snmp_parsing/snmp_parsing_conf.yaml new file mode 100644 index 000000000..aebd1f9cb --- /dev/null +++ b/vitrage/tests/resources/snmp_parsing/snmp_parsing_conf.yaml @@ -0,0 +1,3 @@ +- system_oid: 1.3.6.1.4.1.3902.4101.1.3.1.12 + system: Tecs Director + datasource: vitrage.snmp.event \ No newline at end of file diff --git a/vitrage/tests/unit/snmp_parsing/__init__.py b/vitrage/tests/unit/snmp_parsing/__init__.py index e69de29bb..13d9e0f8b 100644 --- a/vitrage/tests/unit/snmp_parsing/__init__.py +++ b/vitrage/tests/unit/snmp_parsing/__init__.py @@ -0,0 +1,15 @@ +# Copyright 2018 - 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' diff --git a/vitrage/tests/unit/snmp_parsing/test_snmp_parsing.py b/vitrage/tests/unit/snmp_parsing/test_snmp_parsing.py index dea4c711a..fc19e61ae 100644 --- a/vitrage/tests/unit/snmp_parsing/test_snmp_parsing.py +++ b/vitrage/tests/unit/snmp_parsing/test_snmp_parsing.py @@ -12,6 +12,7 @@ # License for the specific language governing permissions and limitations # under the License. +import copy from oslo_config import cfg from pysnmp.proto.rfc1902 import Integer @@ -112,7 +113,8 @@ class TestSnmpParsing(base.BaseTest): cfg.IntOpt('snmp_listening_port', default=8162, help='The listening port of snmp_parsing service'), cfg.StrOpt('oid_mapping', - default='/etc/vitrage/snmp_parsing_conf.yaml', + default='vitrage/tests/resources/snmp_parsing/' + 'snmp_parsing_conf.yaml', help='The default path of oid_mapping yaml file'), ] @@ -127,3 +129,16 @@ class TestSnmpParsing(base.BaseTest): parsing_service = SnmpParsingService(self.conf) dict_converted = parsing_service._convert_binds_to_dict(BINDS_REPORTED) self.assertEqual(dict_converted, DICT_EXPECTED) + + def test_get_event_type(self): + parsing_service = SnmpParsingService(self.conf) + event_type = parsing_service._get_event_type(DICT_EXPECTED) + self.assertEqual(event_type, 'vitrage.snmp.event') + + def test_converted_trap_mapping_diff_system(self): + converted_trap_diff_sys = copy.copy(DICT_EXPECTED) + converted_trap_diff_sys.update( + {u'1.3.6.1.4.1.3902.4101.1.3.1.12': u'Different System'}) + parsing_service = SnmpParsingService(self.conf) + event_type = parsing_service._get_event_type(converted_trap_diff_sys) + self.assertIsNone(event_type)