snmp notifier support

Change-Id: I7717f9815b1791ca61f0c91d873f2f5a30bb11f8
This commit is contained in:
Anna Reznikov 2017-02-23 14:28:00 +00:00
parent 93ee110de0
commit 961fc9d52c
20 changed files with 821 additions and 1 deletions

View File

@ -47,3 +47,5 @@ Configuration
* `Zabbix Configuration <http://docs.openstack.org/developer/vitrage/zabbix_vitrage.html>`_
* `SNMP Sender Configuration <https://github.com/openstack/vitrage/blob/master/doc/source/notifier-snmp-plugin.rst>`_

View File

@ -0,0 +1,103 @@
===============================
Vitrage Notifier plugins - SNMP
===============================
Overview
========
The Evaluator may determine that an alarm should be created, deleted or otherwise updated.
Other components are notified of such changes by the Vitrage Notifier service. Among others, Vitrage Notifier is responsible for sending snmp traps for raised and deleted deduced alarms.
This document describes the implementation of generating SNMP Traps on Vitrage alarms.
You can find a description of the Vitrage Notifier infrastructure in the documentation file `notifier-aodh-plugin.rst <https://github.com/openstack/vitrage/blob/master/doc/source/notifier-aodh-plugin.rst>`_.
SNMP Plugin
===========
The OIDs of the SNMP traps on Vitrage alarms should correspond to the definitions in the MIB file(s) used by the relevant companies.
The traps should be sent on activation and deactivation of alarms.
In order to use the SNMP plugin:
--------------------------------
1. The default SNMP sender: ``vitrage.notifier.plugins.snmp.snmp_sender.py``, in order to use it:
- Add to ``vitrage.conf``:
* notifiers = snmp
* [snmp]
consumers = <path to consumers yaml file>
alarm_oid_mapping = <path to alarm oid mapping yaml file>
oid_tree = <path to tree format oid configuration yaml file>
- ``consumers`` file should be in the following format::
- host:
name: <subscriber name>
send_to: <subscriber ip>
port: <subscriber port>
community: <community string: for example public>
There can be more then one host
- ``alarm_oid_mapping`` file should be in the following format:
For each alarm::
<headline>:
oid: '.<number>'
alarm_name: <alarm name as appears in Vitrage deduced alarms>
- ``oid_tree`` file should be in the following format::
severity_mapping:
<mapped severity>: <number>
snmp_tree:
root:
oid: <num.num....>
next:
node:
oid: <num.num....>
next:
...
next:
node:
oid: <num.num....>
next:
node:
oid: <num.num....>
with_values: 1
next:
leaf:
oid: <num.num....>
leaf2:
oid: <num.num....>
...
node:
oid: <num.num....>
next:
...
next:
node
oid: <num.num....>
next:
ALARM_OID:
oid: <no num>
next:
SEVERITY: - optional
oid: <no num>
"with_values" defines the parameters which's values should be sent in the snmp trap. If it's value is "1" then all it's children's values will be sent in the snmp trap.
SEVERITY is an optional parameter, if it exists then severity mapping should exist
2. Optional: for defining other SNMP sender:
- Create a package with SNMP sender
- New SNMP sender should inherit from abstract class: ``vitrage.vitrage.notifier.plugins.snmp.base.py``
- Define the package in vitrage.conf under [snmp] section:
* snmp_sender_class = <Snmp sender class location>

View File

@ -27,3 +27,4 @@ keystonemiddleware>=4.12.0 # Apache-2.0
stevedore>=1.20.0 # Apache-2.0
voluptuous>=0.8.9 # BSD License
sympy>=0.7.6 # BSD
pysnmp>=4.2.3,<5.0.0 # BSD

View File

@ -32,6 +32,7 @@ stevedore>=1.20.0 # Apache-2.0
voluptuous>=0.8.9 # BSD License
sympy>=0.7.6 # BSD
reno>=1.8.0 # Apache-2.0
pysnmp>=4.2.3,<5.0.0 # BSD
# Doc requirements
openstackdocstheme>=1.5.0 # Apache-2.0

View File

@ -16,7 +16,7 @@ from oslo_config import cfg
OPTS = [
cfg.ListOpt('notifiers',
help='Names of enabled notifiers (example aodh, nova)'),
help='Names of enabled notifiers (example aodh, nova, snmp)'),
cfg.ListOpt('notifiers_path',
default=['vitrage.notifier.plugins'],
help='list of base path for notifiers'),

View File

@ -0,0 +1,37 @@
# 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 oslo_config import cfg
OPTS = [
cfg.StrOpt('notifier',
default='vitrage.notifier.plugins.snmp.'
'snmp_notifier.SnmpNotifier',
help='snmp notifier class path',
required=True),
cfg.StrOpt('snmp_sender_class',
default='vitrage.notifier.plugins.snmp.'
'snmp_sender.SnmpSender',
help='snmp sender class path',
required=True),
cfg.StrOpt('alarm_oid_mapping',
default='',
help='alarm oid mapping yaml file path'),
cfg.StrOpt('consumers',
default='',
help='consumers yaml file path'),
cfg.StrOpt('oid_tree',
default='',
help='path to oid tree format configuration yaml file'),
]

View File

@ -0,0 +1,28 @@
# 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 abc
import six
@six.add_metaclass(abc.ABCMeta)
class SnmpSenderBase(object):
"""Abstract Vitrage snmp trap sender"""
def __init__(self, conf):
self.conf = conf
@abc.abstractmethod
def send_snmp(self, alarm_data):
pass

View File

@ -0,0 +1,61 @@
# 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 oslo_log import log as logging
from oslo_utils import importutils
from vitrage.common.constants import NotifierEventTypes
from vitrage.common.constants import VertexProperties as VProps
from vitrage.notifier.plugins.base import NotifierBase
LOG = logging.getLogger(__name__)
class SnmpNotifier(NotifierBase):
@staticmethod
def get_notifier_name():
return 'snmp'
def __init__(self, conf):
super(SnmpNotifier, self).__init__(conf)
self.snmp_sender = \
importutils.import_object(conf.snmp.snmp_sender_class, conf)
def process_event(self, data, event_type):
if event_type == NotifierEventTypes.ACTIVATE_DEDUCED_ALARM_EVENT \
or event_type == \
NotifierEventTypes.DEACTIVATE_DEDUCED_ALARM_EVENT:
LOG.info('Vitrage snmp Info: Sending snmp trap')
try:
self.snmp_sender.send_snmp(self._parse_alarm_data(data))
except Exception as e:
LOG.exception('Vitrage snmp Error: '
'Failed to send SNMP trap: %s', e)
return
@staticmethod
def _parse_alarm_data(data):
new_data_format = {}
for key, val in data.items():
new_data_format[key] = val
if key == VProps.RESOURCE:
for k, v in val.items():
new_data_format[VProps.RESOURCE + '_' + str(k)] = v
return new_data_format

View File

@ -0,0 +1,195 @@
# 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 oslo_log import log as logging
from pysnmp.entity.engine import SnmpEngine
from pysnmp.hlapi.asyncore.sync.compat.ntforg import sendNotification
from pysnmp.hlapi.asyncore.transport import UdpTransportTarget
from pysnmp.hlapi.auth import CommunityData
from pysnmp.hlapi.context import ContextData
from pysnmp.proto.rfc1902 import OctetString
from pysnmp.smi.rfc1902 import NotificationType
from pysnmp.smi.rfc1902 import ObjectIdentity
from vitrage.common.constants import VertexProperties as VProps
from vitrage.notifier.plugins.snmp.base import SnmpSenderBase
from vitrage.utils.file import load_yaml_file
LOG = logging.getLogger(__name__)
# TODO(annarez): change NA to N/A
NA = 'NA'
SNMP_TREE = 'snmp_tree'
SEVERITY_MAPPING = 'severity_mapping'
OID = 'oid'
NEXT = 'next'
WITH_VALS = 'with_values'
SEVERITY = 'SEVERITY'
ALARM_OID = 'ALARM_OID'
class SnmpSender(SnmpSenderBase):
def __init__(self, conf):
super(SnmpSender, self).__init__(conf)
self.hosts = load_yaml_file(self.conf.snmp.consumers, True)
self.oid_tree = load_yaml_file(self.conf.snmp.oid_tree, True)
self.alarm_mapping = \
load_yaml_file(self.conf.snmp.alarm_oid_mapping, True)
self.oids, self.var_fields = self._build_oids()
def send_snmp(self, alarm_data):
alert_details, alert_severity_oid = self._get_details(alarm_data)
if alert_details:
alarm_oid = \
self._get_alert_oid(alert_details[OID], alert_severity_oid)
if not alarm_oid:
return
for host in self.hosts:
self._send_snmp_trap(host,
self._get_var_binds(alarm_data),
alarm_oid)
else:
LOG.info('Vitrage snmp Info: Unregconized alarm. Alarm type: %s',
alarm_data[VProps.NAME])
def _get_details(self, alarm_data):
if not (self.hosts and self.alarm_mapping and
self.oids and self.var_fields):
LOG.error('Vitrage snmp Error: definitions is '
'missing from configuration file')
return None, None
alert_severity_oid = self._get_severity_oid(alarm_data)
if not alert_severity_oid and \
self.oids.get(SEVERITY):
LOG.error('Vitrage snmp Error: there '
'is no severity mapping in file')
return None, None
alarm_name = alarm_data.get(VProps.NAME)
alert_details = self.alarm_mapping.get(alarm_name)
return alert_details, alert_severity_oid
def _build_oids(self):
if not self.oid_tree:
return None, None
oids_dict, var_binds = \
self._build_oid_recursively('', self.oid_tree[SNMP_TREE],
{}, [], 0)
oids_dict = {key: oids_dict[key][1:] for key in oids_dict}
return oids_dict, var_binds
def _build_oid_recursively(self, oid, curr, oids_dict,
var_binds, is_with_val):
for key in curr:
new_oid = oid + '.' + curr[key][OID]
next_node = curr[key].get(NEXT)
if not next_node:
if is_with_val:
var_binds.append(key)
oids_dict[key] = new_oid
else:
with_val = curr[key].get(WITH_VALS, 0)
self._build_oid_recursively(new_oid, next_node, oids_dict,
var_binds, with_val)
return oids_dict, var_binds
def _get_var_binds(self, alert_values):
var_binds = [(self.oids[field],
OctetString(alert_values.get(field, NA)))
for field in self.var_fields]
return var_binds
def _get_alert_oid(self, alert_oid, severity_oid):
sev_oid = self.oids.get(SEVERITY)
alarm_oid = self.oids.get(ALARM_OID)
if not (sev_oid or alarm_oid):
LOG.error("Vitrage snmp Error: snmp tree incorrect format")
return None
if severity_oid:
oid = sev_oid.replace('..', alert_oid + '.' + severity_oid)
return oid
else:
oid = alarm_oid[:-1] + alert_oid
return oid
def _get_severity_oid(self, alert_values):
severity_mapping = self.oid_tree.get(SEVERITY_MAPPING)
if not severity_mapping:
return None
alarm_severity = alert_values.get(VProps.OPERATIONAL_SEVERITY)
state = alert_values.get(VProps.STATE)
if state in severity_mapping:
return severity_mapping[state]
elif alarm_severity in severity_mapping:
return severity_mapping[alarm_severity]
else:
LOG.debug('Vitrage snmp Debug: Unsupported alarm severity')
return None
@staticmethod
def _send_snmp_trap(host, var_binds, alarm_oid):
host_details = host['host']
send_to = str(host_details['send_to'])
port = str(host_details.get('port', 162))
community_str = host_details.get('community', 'public')
LOG.debug("Vitrage snmp Debug: Trap parameters: send_to: %s, "
"port: %s, community string: %s" %
(send_to, port, community_str))
error_indication, error_status, error_index, var_bins = next(
sendNotification(
SnmpEngine(),
CommunityData(community_str, mpModel=1),
UdpTransportTarget((send_to, port)),
ContextData(),
'trap',
NotificationType(
ObjectIdentity(alarm_oid),
).addVarBinds(*var_binds)
)
)
if error_indication:
LOG.error('Vitrage snmp Error: Notification not sent: %s' %
error_indication)
elif error_status:
LOG.error('Vitrage snmp Error: Notification Receiver '
'returned error: %s @%s' %
(error_status, error_index))

View File

@ -24,6 +24,7 @@ import vitrage.entity_graph.consistency
import vitrage.evaluator
import vitrage.keystone_client
import vitrage.notifier
import vitrage.notifier.plugins.snmp
import vitrage.os_clients
import vitrage.rpc
@ -43,6 +44,7 @@ def list_opts():
('consistency', vitrage.entity_graph.consistency.OPTS),
('entity_graph', vitrage.entity_graph.OPTS),
('service_credentials', vitrage.keystone_client.OPTS),
('snmp', vitrage.notifier.plugins.snmp.OPTS),
('DEFAULT', itertools.chain(
vitrage.os_clients.OPTS,
vitrage.rpc.OPTS,

View File

@ -0,0 +1,3 @@
VM network problem:
oid: '.100000'
alarm_name: vitrageDeducedTest

View File

@ -0,0 +1,5 @@
- host:
name: kuku
send_to: 1.1.1.1
port: 162
community: public

View File

@ -0,0 +1,29 @@
severity_mapping:
CRITICAL: 3
snmp_tree:
general:
oid: 1.3.6.1.4.1
next:
company:
oid: 1.1.1
next:
ALARM_OBJECTS:
oid: 1
with_values: 1
next:
name:
oid: 1
is_deleted:
oid: 2
operational_severity:
oid: 3
ALARM_PREFIX:
oid: 2
next:
ALARM_OID:
oid:
next:
SEVERITY:
oid:

View File

@ -0,0 +1,22 @@
snmp_tree:
general:
oid: 1.3.6.1.4.1
next:
company:
oid: 1.1.1
next:
ALARM_OBJECTS:
oid: 1
with_values: 1
next:
name:
oid: 1
is_deleted:
oid: 2
operational_severity:
oid: 3
ALARM_PREFIX:
oid: 2
next:
ALARM_OID:
oid:

View File

@ -0,0 +1,15 @@
# 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.
__author__ = 'stack'

View File

@ -0,0 +1,43 @@
# 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.common.constants import VertexProperties as VProps
false_ = 'False'
name_ = 'VM network problem'
category_ = 'ALARM'
critical_ = 'CRITICAL'
GENERAL_OID = '1.3.6.1.4.1'
COMPANY_OID = '1.1.1'
ALARM_OBJECTS_OID = '1'
ALARM_PREFIX_OID = '2'
NAME_OID = '1'
IS_DELETED_OID = '2'
SEVERITY_OID = '3'
ALERT_OID = '.100000'
alarm_data = {VProps.CATEGORY: category_,
VProps.NAME: name_,
VProps.RESOURCE + '_' + VProps.IS_DELETED: false_,
VProps.RESOURCE + '_' + VProps.IS_PLACEHOLDER: false_,
VProps.IS_DELETED: false_,
VProps.OPERATIONAL_SEVERITY: critical_,
VProps.RESOURCE:
{VProps.IS_PLACEHOLDER: false_,
VProps.IS_DELETED: false_}}
alert_details = {'oid': ALERT_OID, 'alarm_name': 'vitrageDeducedTest'}

View File

@ -0,0 +1,50 @@
# 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.common.constants import VertexProperties as VProps
from vitrage.graph import Vertex
from vitrage.notifier.plugins.snmp.snmp_notifier import SnmpNotifier
from vitrage.tests import base
from vitrage.tests.unit.notifier.snmp_notifier import common
class SnmpNotifierTest(base.BaseTest):
@classmethod
def setUpClass(cls):
cls.resource_props = {VProps.IS_DELETED: common.false_,
VProps.IS_PLACEHOLDER: common.false_}
cls.props = {VProps.IS_DELETED: common.false_,
VProps.NAME: common.name_,
VProps.RESOURCE: cls.resource_props,
VProps.CATEGORY: common.category_,
VProps.OPERATIONAL_SEVERITY: common.critical_}
cls.alarm_vertex = Vertex('RESOURCE:nova.instance:test1', cls.props)
def test_parse_alarm(self):
alarm_data = SnmpNotifier._parse_alarm_data(self.alarm_vertex)
self.assert_is_not_empty(alarm_data)
self.assertEqual(alarm_data.get(VProps.IS_DELETED), common.false_)
self.assertEqual(alarm_data.get(VProps.NAME), common.name_)
self.assertEqual(alarm_data.get(VProps.CATEGORY), common.category_)
self.assertEqual(alarm_data.get(VProps.OPERATIONAL_SEVERITY),
common.critical_)
self.assertEqual(alarm_data.get(VProps.RESOURCE + '_' +
VProps.IS_DELETED), common.false_)
self.assertEqual(alarm_data.get(VProps.RESOURCE + '_' +
VProps.IS_PLACEHOLDER), common.false_)

View File

@ -0,0 +1,122 @@
# 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 oslo_config import cfg
from pysnmp.proto.rfc1902 import OctetString
from vitrage.common.constants import VertexProperties as VProps
import vitrage.notifier.plugins.snmp.snmp_sender as sender
from vitrage.notifier.plugins.snmp.snmp_sender import SnmpSender
from vitrage.tests import base
from vitrage.tests.mocks import utils
from vitrage.tests.unit.notifier.snmp_notifier import common
class SnmpNotifierTest(base.BaseTest):
simple_opts = [
cfg.StrOpt('notifier',
default='vitrage.notifier.plugins.snmp.'
'snmp_notifier.SnmpNotifier',
required=True),
cfg.StrOpt('snmp_sender_class',
default='vitrage.notifier.plugins.snmp.'
'snmp_sender.SnmpSender',
required=True),
cfg.StrOpt('alarm_oid_mapping',
default=utils.get_resources_dir() +
'/snmp_notifier/alarm_oid_mapping.yaml'),
cfg.StrOpt('consumers',
default=utils.get_resources_dir() +
'/snmp_notifier/dests.yaml'),
cfg.StrOpt('oid_tree',
default=utils.get_resources_dir() +
'/snmp_notifier/'
'oid_tree_with_severity_mapping.yaml'),
]
@classmethod
def setUpClass(cls):
cls.conf = cfg.ConfigOpts()
cls.conf.register_opts(cls.simple_opts, group='snmp')
cls.snmp_sender = SnmpSender(cls.conf)
def test_create_oids(self):
oids, var_lst = self.snmp_sender._build_oids()
self.assertEqual(len(oids), 4)
self.assertEqual(len(var_lst), 3)
self.assertIn(VProps.NAME, oids)
self.assertIn(VProps.IS_DELETED, oids)
self.assertIn(VProps.OPERATIONAL_SEVERITY, oids)
self.assertIn(sender.SEVERITY, oids)
self.assertIn(VProps.NAME, var_lst)
self.assertIn(VProps.IS_DELETED, var_lst)
self.assertIn(VProps.OPERATIONAL_SEVERITY, var_lst)
def test_var_binds(self):
oid_with_alarm_objects = \
common.GENERAL_OID + '.' + \
common.COMPANY_OID + '.' + common.ALARM_OBJECTS_OID
var_binds = self.snmp_sender._get_var_binds(common.alarm_data)
self.assertEqual(len(var_binds), 3)
self.assertIn((oid_with_alarm_objects + '.' + common.NAME_OID,
OctetString(common.alarm_data.get(VProps.NAME,
sender.NA))),
var_binds)
self.assertIn((oid_with_alarm_objects + '.' + common.IS_DELETED_OID,
OctetString(common.alarm_data.get
(VProps.IS_DELETED, sender.NA))), var_binds)
self.assertIn((oid_with_alarm_objects + '.' + common.SEVERITY_OID,
OctetString(common.alarm_data.get
(VProps.OPERATIONAL_SEVERITY, sender.NA))),
var_binds)
def test_get_severity_oid(self):
alert_severity_oid = \
self.snmp_sender._get_severity_oid(common.alarm_data)
self.assertEqual(alert_severity_oid, common.SEVERITY_OID)
def test_get_alert_oid(self):
alert_severity_oid = \
self.snmp_sender._get_severity_oid(common.alarm_data)
alert_details = self.snmp_sender.alarm_mapping.get(common.name_)
# TODO(annarez): check if I need this assert
self.assertEqual(alert_details.get(sender.OID), common.ALERT_OID)
alert_oid = self.snmp_sender._get_alert_oid(alert_details[sender.OID],
alert_severity_oid)
self.assertEqual(alert_oid, common.GENERAL_OID + '.' +
common.COMPANY_OID + '.' + common.ALARM_PREFIX_OID +
common.ALERT_OID + '.' + common.SEVERITY_OID)
def test_get_details(self):
alert_details, alert_severity_oid = \
self.snmp_sender._get_details(common.alarm_data)
self.assertEqual(alert_details, common.alert_details)
self.assertEqual(alert_severity_oid, common.SEVERITY_OID)

View File

@ -0,0 +1,97 @@
# 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 oslo_config import cfg
from vitrage.common.constants import VertexProperties as VProps
import vitrage.notifier.plugins.snmp.snmp_sender as sender
from vitrage.notifier.plugins.snmp.snmp_sender import SnmpSender
from vitrage.tests import base
from vitrage.tests.mocks import utils
from vitrage.tests.unit.notifier.snmp_notifier import common
class SnmpNotifierTest(base.BaseTest):
simple_opts = [
cfg.StrOpt('notifier',
default='vitrage.notifier.plugins.snmp.'
'snmp_notifier.SnmpNotifier',
required=True),
cfg.StrOpt('snmp_sender_class',
default='vitrage.notifier.plugins.snmp.'
'snmp_sender.SnmpSender',
required=True),
cfg.StrOpt('alarm_oid_mapping',
default=utils.get_resources_dir() +
'/snmp_notifier/alarm_oid_mapping.yaml'),
cfg.StrOpt('consumers',
default=utils.get_resources_dir() +
'/snmp_notifier/dests.yaml'),
cfg.StrOpt('oid_tree',
default=utils.get_resources_dir() +
'/snmp_notifier/'
'oid_tree_without_severity_mapping.yaml'),
]
@classmethod
def setUpClass(cls):
cls.conf = cfg.ConfigOpts()
cls.conf.register_opts(cls.simple_opts, group='snmp')
cls.snmp_sender = SnmpSender(cls.conf)
def test_create_oids(self):
oids, var_lst = self.snmp_sender._build_oids()
self.assertEqual(len(oids), 4)
self.assertEqual(len(var_lst), 3)
self.assertIn(VProps.NAME, oids)
self.assertIn(VProps.IS_DELETED, oids)
self.assertIn(VProps.OPERATIONAL_SEVERITY, oids)
self.assertIn(sender.ALARM_OID, oids)
self.assertIn(VProps.NAME, var_lst)
self.assertIn(VProps.IS_DELETED, var_lst)
self.assertIn(VProps.OPERATIONAL_SEVERITY, var_lst)
def test_get_severity_oid(self):
alert_severity_oid = \
self.snmp_sender._get_severity_oid(common.alarm_data)
self.assertEqual(alert_severity_oid, None)
def test_get_alert_oid(self):
alert_severity_oid = \
self.snmp_sender._get_severity_oid(common.alarm_data)
alert_details = self.snmp_sender.alarm_mapping.get(common.name_)
self.assertEqual(alert_details.get(sender.OID), common.ALERT_OID)
alert_oid = self.snmp_sender._get_alert_oid(alert_details[sender.OID],
alert_severity_oid)
self.assertEqual(alert_oid, common.GENERAL_OID + '.' +
common.COMPANY_OID + '.' + common.ALARM_PREFIX_OID +
common.ALERT_OID)
def test_get_details(self):
alert_details, alert_severity_oid = \
self.snmp_sender._get_details(common.alarm_data)
self.assertEqual(alert_details, common.alert_details)
self.assertEqual(alert_severity_oid, None)

View File

@ -55,6 +55,10 @@ def load_yaml_files(dir_path, with_exception=False):
def load_yaml_file(full_path, with_exception=False):
if not os.path.isfile(full_path):
LOG.error("File doesn't exist: %s." % full_path)
return None
with open(full_path, 'r') as stream:
try:
return yaml.load(stream, Loader=yaml.BaseLoader)