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>`_
|
* `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
|
stevedore>=1.20.0 # Apache-2.0
|
||||||
voluptuous>=0.8.9 # BSD License
|
voluptuous>=0.8.9 # BSD License
|
||||||
sympy>=0.7.6 # BSD
|
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
|
voluptuous>=0.8.9 # BSD License
|
||||||
sympy>=0.7.6 # BSD
|
sympy>=0.7.6 # BSD
|
||||||
reno>=1.8.0 # Apache-2.0
|
reno>=1.8.0 # Apache-2.0
|
||||||
|
pysnmp>=4.2.3,<5.0.0 # BSD
|
||||||
|
|
||||||
# Doc requirements
|
# Doc requirements
|
||||||
openstackdocstheme>=1.5.0 # Apache-2.0
|
openstackdocstheme>=1.5.0 # Apache-2.0
|
||||||
|
@ -16,7 +16,7 @@ from oslo_config import cfg
|
|||||||
|
|
||||||
OPTS = [
|
OPTS = [
|
||||||
cfg.ListOpt('notifiers',
|
cfg.ListOpt('notifiers',
|
||||||
help='Names of enabled notifiers (example aodh, nova)'),
|
help='Names of enabled notifiers (example aodh, nova, snmp)'),
|
||||||
cfg.ListOpt('notifiers_path',
|
cfg.ListOpt('notifiers_path',
|
||||||
default=['vitrage.notifier.plugins'],
|
default=['vitrage.notifier.plugins'],
|
||||||
help='list of base path for notifiers'),
|
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.evaluator
|
||||||
import vitrage.keystone_client
|
import vitrage.keystone_client
|
||||||
import vitrage.notifier
|
import vitrage.notifier
|
||||||
|
import vitrage.notifier.plugins.snmp
|
||||||
import vitrage.os_clients
|
import vitrage.os_clients
|
||||||
import vitrage.rpc
|
import vitrage.rpc
|
||||||
|
|
||||||
@ -43,6 +44,7 @@ def list_opts():
|
|||||||
('consistency', vitrage.entity_graph.consistency.OPTS),
|
('consistency', vitrage.entity_graph.consistency.OPTS),
|
||||||
('entity_graph', vitrage.entity_graph.OPTS),
|
('entity_graph', vitrage.entity_graph.OPTS),
|
||||||
('service_credentials', vitrage.keystone_client.OPTS),
|
('service_credentials', vitrage.keystone_client.OPTS),
|
||||||
|
('snmp', vitrage.notifier.plugins.snmp.OPTS),
|
||||||
('DEFAULT', itertools.chain(
|
('DEFAULT', itertools.chain(
|
||||||
vitrage.os_clients.OPTS,
|
vitrage.os_clients.OPTS,
|
||||||
vitrage.rpc.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):
|
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:
|
with open(full_path, 'r') as stream:
|
||||||
try:
|
try:
|
||||||
return yaml.load(stream, Loader=yaml.BaseLoader)
|
return yaml.load(stream, Loader=yaml.BaseLoader)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user