From b8cc22c719e71bd2a5976420f68c745d5a831d99 Mon Sep 17 00:00:00 2001 From: xupeipei Date: Thu, 28 Dec 2017 19:04:22 +0800 Subject: [PATCH] Add parsing of snmp_traps for snmp_parsing service Add paring of snmp traps and unittest, the tempest test will be submitted with sending trap to rabbitmq. Implements: blueprint snmp-support Change-Id: Ibb4f4294dd95bfd1a37e9291318b2775bf1fedd3 Signed-off-by: xupeipei --- vitrage/snmp_parsing/service.py | 40 +++++- vitrage/tests/unit/snmp_parsing/__init__.py | 0 .../unit/snmp_parsing/test_snmp_parsing.py | 129 ++++++++++++++++++ 3 files changed, 166 insertions(+), 3 deletions(-) create mode 100644 vitrage/tests/unit/snmp_parsing/__init__.py create mode 100644 vitrage/tests/unit/snmp_parsing/test_snmp_parsing.py diff --git a/vitrage/snmp_parsing/service.py b/vitrage/snmp_parsing/service.py index ddf590f73..e07901578 100644 --- a/vitrage/snmp_parsing/service.py +++ b/vitrage/snmp_parsing/service.py @@ -12,13 +12,16 @@ # License for the specific language governing permissions and limitations # under the License. +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 LOG = log.getLogger(__name__) @@ -68,5 +71,36 @@ class SnmpParsingService(os_service.Service): # noinspection PyUnusedLocal def callback_func(self, transport_dispatcher, transport_domain, transport_address, whole_msg): - # TODO(peipei): need to parse wholeMsg and send to message queue - pass + while whole_msg: + msg_ver = int(snmp_api.decodeMessageVersion(whole_msg)) + if msg_ver in snmp_api.protoModules: + p_mod = snmp_api.protoModules[msg_ver] + else: + LOG.error('Unsupported SNMP version %s.' % msg_ver) + return + req_msg, whole_msg = decoder.decode( + whole_msg, asn1Spec=p_mod.Message(), + ) + req_pdu = p_mod.apiMessage.getPDU(req_msg) + if req_pdu.isSameTypeWith(p_mod.TrapPDU()): + ver_binds = p_mod.apiTrapPDU.getVarBinds(req_pdu) \ + if msg_ver == snmp_api.protoVersion1 \ + 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 + + def _convert_binds_to_dict(self, var_binds): + binds_dict = {} + for oid, val in var_binds: + u_oid = self._convert_obj_to_unicode(oid) + binds_dict[u_oid] = int(val) if type(val) == Integer \ + else self._convert_obj_to_unicode(val) + return binds_dict + + @staticmethod + def _convert_obj_to_unicode(val): + if sys.version_info[0] < 3: + return str(val).decode('iso-8859-1') + return str(val) diff --git a/vitrage/tests/unit/snmp_parsing/__init__.py b/vitrage/tests/unit/snmp_parsing/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/vitrage/tests/unit/snmp_parsing/test_snmp_parsing.py b/vitrage/tests/unit/snmp_parsing/test_snmp_parsing.py new file mode 100644 index 000000000..dea4c711a --- /dev/null +++ b/vitrage/tests/unit/snmp_parsing/test_snmp_parsing.py @@ -0,0 +1,129 @@ +# 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. + +from oslo_config import cfg + +from pysnmp.proto.rfc1902 import Integer +from pysnmp.proto.rfc1902 import ObjectIdentifier +from pysnmp.proto.rfc1902 import ObjectName +from pysnmp.proto.rfc1902 import OctetString +from pysnmp.proto.rfc1902 import TimeTicks + +from vitrage.snmp_parsing.service import SnmpParsingService +from vitrage.tests import base + + +BINDS_REPORTED = [ + (ObjectName('1.3.6.1.2.1.1.3.0'), TimeTicks(1491462248)), + (ObjectName('1.3.6.1.6.3.1.1.4.1.0'), + ObjectIdentifier('1.3.6.1.4.1.3902.4101.1.4.1.2')), + (ObjectName('1.3.6.1.4.1.3902.4101.1.3.1.3'), + OctetString(hexValue='07e10406070408002b0800')), + (ObjectName('1.3.6.1.4.1.3902.4101.1.1.2'), Integer(0)), + (ObjectName('1.3.6.1.4.1.3902.4101.1.1.4'), Integer(0)), + (ObjectName('1.3.6.1.4.1.3902.4101.1.1.3'), OctetString('')), + (ObjectName('1.3.6.1.4.1.3902.4101.1.3.1.11'), OctetString('3305115653')), + (ObjectName('1.3.6.1.4.1.3902.4101.1.3.1.2'), OctetString('host')), + (ObjectName('1.3.6.1.4.1.3902.4101.1.3.1.4'), Integer(1)), + (ObjectName('1.3.6.1.4.1.3902.4101.1.3.1.5'), Integer(14)), + (ObjectName('1.3.6.1.4.1.3902.4101.1.3.1.6'), Integer(0)), + (ObjectName('1.3.6.1.4.1.3902.4101.1.3.1.7'), OctetString('')), + (ObjectName('1.3.6.1.4.1.3902.4101.1.3.1.8'), + OctetString('vimid=,hid=controller_controller,' + 'hostname=controller,' + 'Reason: nova-compute is not available')), + (ObjectName('1.3.6.1.4.1.3902.4101.1.3.1.12'), + OctetString('Tecs Director')), + (ObjectName('1.3.6.1.4.1.3902.4101.1.3.1.9'), Integer(1581)), + (ObjectName('1.3.6.1.4.1.3902.4101.1.3.1.1'), + OctetString('3e7393db-2def-447c-8cba-77bf29ab29b4')), + (ObjectName('1.3.6.1.4.1.3902.4101.1.3.1.14'), + OctetString('compute is not available')), + (ObjectName('1.3.6.1.4.1.3902.4101.1.3.1.15'), + OctetString('vimid=,hostname=controller')), + (ObjectName('1.3.6.1.4.1.3902.4101.1.3.1.16'), OctetString('')), + (ObjectName('1.3.6.1.4.1.3902.4101.1.3.1.17'), + OctetString('10.62.89.92')), + (ObjectName('1.3.6.1.4.1.3902.4101.1.3.1.18'), Integer(0)), + (ObjectName('1.3.6.1.4.1.3902.4101.1.3.1.19'), OctetString('')), + (ObjectName('1.3.6.1.4.1.3902.4101.1.3.1.20'), + OctetString('Asia/Harbin')), + (ObjectName('1.3.6.1.4.1.3902.4101.1.3.1.21'), Integer(0)), + (ObjectName('1.3.6.1.4.1.3902.4101.1.3.1.22'), Integer(0)), + (ObjectName('1.3.6.1.4.1.3902.4101.1.3.1.23'), OctetString('')), + (ObjectName('1.3.6.1.4.1.3902.4101.1.3.1.24'), OctetString('')), + (ObjectName('1.3.6.1.4.1.3902.4101.1.3.1.26'), + OctetString('controller_controller')), + (ObjectName('1.3.6.1.4.1.3902.4101.1.3.1.10'), OctetString('')), + (ObjectName('1.3.6.1.4.1.3902.4101.1.3.1.25'), OctetString('')) +] + +DICT_EXPECTED = { + u'1.3.6.1.4.1.3902.4101.1.3.1.8': + u'vimid=,hid=controller_controller,' + u'hostname=controller,' + u'Reason: nova-compute is not available', + u'1.3.6.1.4.1.3902.4101.1.3.1.9': 1581, + u'1.3.6.1.4.1.3902.4101.1.3.1.6': 0, + u'1.3.6.1.4.1.3902.4101.1.3.1.7': u'', + u'1.3.6.1.4.1.3902.4101.1.3.1.4': 1, + u'1.3.6.1.4.1.3902.4101.1.3.1.5': 14, + u'1.3.6.1.4.1.3902.4101.1.3.1.2': u'host', + u'1.3.6.1.4.1.3902.4101.1.3.1.3': + u'\x07\xe1\x04\x06\x07\x04\x08\x00+\x08\x00', + u'1.3.6.1.4.1.3902.4101.1.3.1.1': u'3e7393db-2def-447c-8cba-77bf29ab29b4', + u'1.3.6.1.4.1.3902.4101.1.3.1.18': 0, + u'1.3.6.1.4.1.3902.4101.1.3.1.19': u'', + u'1.3.6.1.4.1.3902.4101.1.3.1.10': u'', + u'1.3.6.1.4.1.3902.4101.1.3.1.11': u'3305115653', + u'1.3.6.1.4.1.3902.4101.1.3.1.12': u'Tecs Director', + u'1.3.6.1.4.1.3902.4101.1.3.1.14': u'compute is not available', + u'1.3.6.1.4.1.3902.4101.1.3.1.15': u'vimid=,hostname=controller', + u'1.3.6.1.4.1.3902.4101.1.3.1.16': u'', + u'1.3.6.1.4.1.3902.4101.1.3.1.17': u'10.62.89.92', + u'1.3.6.1.4.1.3902.4101.1.1.4': 0, + u'1.3.6.1.4.1.3902.4101.1.1.3': u'', + u'1.3.6.1.4.1.3902.4101.1.1.2': 0, + u'1.3.6.1.2.1.1.3.0': u'1491462248', + u'1.3.6.1.6.3.1.1.4.1.0': u'1.3.6.1.4.1.3902.4101.1.4.1.2', + u'1.3.6.1.4.1.3902.4101.1.3.1.25': u'', + u'1.3.6.1.4.1.3902.4101.1.3.1.24': u'', + u'1.3.6.1.4.1.3902.4101.1.3.1.26': u'controller_controller', + u'1.3.6.1.4.1.3902.4101.1.3.1.21': 0, + u'1.3.6.1.4.1.3902.4101.1.3.1.20': u'Asia/Harbin', + u'1.3.6.1.4.1.3902.4101.1.3.1.23': u'', + u'1.3.6.1.4.1.3902.4101.1.3.1.22': 0 +} + + +class TestSnmpParsing(base.BaseTest): + OPTS = [ + 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', + help='The default path of oid_mapping yaml file'), + ] + + # noinspection PyPep8Naming + @classmethod + def setUpClass(cls): + super(TestSnmpParsing, cls).setUpClass() + cls.conf = cfg.ConfigOpts() + cls.conf.register_opts(cls.OPTS, group='snmp_parsing') + + def test_convert_binds_to_dict(self): + parsing_service = SnmpParsingService(self.conf) + dict_converted = parsing_service._convert_binds_to_dict(BINDS_REPORTED) + self.assertEqual(dict_converted, DICT_EXPECTED)