snmp notifier support
Change-Id: I7717f9815b1791ca61f0c91d873f2f5a30bb11f8
This commit is contained in:
parent
93ee110de0
commit
961fc9d52c
@ -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>`_
|
||||
|
||||
|
103
doc/source/notifier-snmp-plugin.rst
Normal file
103
doc/source/notifier-snmp-plugin.rst
Normal 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>
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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'),
|
||||
|
37
vitrage/notifier/plugins/snmp/__init__.py
Normal file
37
vitrage/notifier/plugins/snmp/__init__.py
Normal 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'),
|
||||
]
|
28
vitrage/notifier/plugins/snmp/base.py
Normal file
28
vitrage/notifier/plugins/snmp/base.py
Normal 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
|
61
vitrage/notifier/plugins/snmp/snmp_notifier.py
Normal file
61
vitrage/notifier/plugins/snmp/snmp_notifier.py
Normal 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
|
195
vitrage/notifier/plugins/snmp/snmp_sender.py
Normal file
195
vitrage/notifier/plugins/snmp/snmp_sender.py
Normal 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))
|
@ -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,
|
||||
|
@ -0,0 +1,3 @@
|
||||
VM network problem:
|
||||
oid: '.100000'
|
||||
alarm_name: vitrageDeducedTest
|
5
vitrage/tests/resources/snmp_notifier/dests.yaml
Normal file
5
vitrage/tests/resources/snmp_notifier/dests.yaml
Normal file
@ -0,0 +1,5 @@
|
||||
- host:
|
||||
name: kuku
|
||||
send_to: 1.1.1.1
|
||||
port: 162
|
||||
community: public
|
@ -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:
|
@ -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:
|
15
vitrage/tests/unit/notifier/snmp_notifier/__init__.py
Normal file
15
vitrage/tests/unit/notifier/snmp_notifier/__init__.py
Normal 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'
|
43
vitrage/tests/unit/notifier/snmp_notifier/common.py
Normal file
43
vitrage/tests/unit/notifier/snmp_notifier/common.py
Normal 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'}
|
@ -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_)
|
@ -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)
|
@ -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)
|
@ -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)
|
||||
|
Loading…
Reference in New Issue
Block a user