aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLadue, David (dl3158) <dl3158@att.com>2020-02-14 12:02:30 -0500
committerLadue, David (dl3158) <dl3158@att.com>2020-03-02 11:20:48 -0500
commit9c9a86fe301431ca353f8c249164a854db9399e0 (patch)
tree9a4171fd2fd31ee5312e1b83c1266673ca7c97b1
parent1578ce3e4f0b95babd93cbfa193fe5492bf4591e (diff)
copyright banner changes2.0.3
Change-Id: I64f089e36fb8b21ed1719696ceeded1590ba8f8c Signed-off-by: Ladue, David (dl3158) <dl3158@att.com> Issue-ID: DCAEGEN2-2068 Signed-off-by: Ladue, David (dl3158) <dl3158@att.com>
-rw-r--r--Dockerfile32
-rw-r--r--LICENSE.txt2
-rw-r--r--etc/snmptrapd.json41
-rw-r--r--etc/version.dat1
-rw-r--r--pom.xml2
-rw-r--r--setup.py8
-rw-r--r--snmptrap/__init__.py7
-rwxr-xr-xsnmptrap/healthcheck.sh10
-rw-r--r--snmptrap/mod/__init__.py7
-rw-r--r--snmptrap/mod/trapd_exit.py7
-rw-r--r--snmptrap/mod/trapd_get_cbs_config.py36
-rw-r--r--snmptrap/mod/trapd_http_session.py12
-rw-r--r--snmptrap/mod/trapd_io.py31
-rw-r--r--snmptrap/mod/trapd_runtime_pid.py7
-rw-r--r--snmptrap/mod/trapd_settings.py23
-rw-r--r--snmptrap/mod/trapd_snmpv3.py112
-rw-r--r--snmptrap/mod/trapd_stats_settings.py44
-rw-r--r--snmptrap/mod/trapd_stormwatch.py513
-rw-r--r--snmptrap/mod/trapd_stormwatch_settings.py61
-rw-r--r--snmptrap/mod/trapd_vb_types.py42
-rwxr-xr-xsnmptrap/scheduler.sh186
-rwxr-xr-xsnmptrap/send_hb_trap65
-rw-r--r--snmptrap/snmptrapd.py405
-rwxr-xr-xsnmptrap/snmptrapd.sh324
-rw-r--r--tests/__init__.py2
-rwxr-xr-xtests/py-test-snmptrap.sh26
-rw-r--r--tests/test_snmptrapd.py45
-rwxr-xr-xtests/test_snmptrapd_send_test_trap.py16
-rw-r--r--tests/test_trapd_exit.py16
-rw-r--r--tests/test_trapd_get_cbs_config.py116
-rw-r--r--tests/test_trapd_http_session.py31
-rw-r--r--tests/test_trapd_io.py295
-rw-r--r--tests/test_trapd_runtime_pid.py16
-rw-r--r--tests/test_trapd_settings.py16
-rw-r--r--tests/test_trapd_snmpv3.py16
-rw-r--r--tests/test_trapd_stormwatch.py54
-rw-r--r--tests/test_trapd_stormwatch_settings.py158
-rw-r--r--tests/test_trapd_vb_types.py116
-rw-r--r--version.properties6
39 files changed, 2269 insertions, 638 deletions
diff --git a/Dockerfile b/Dockerfile
index f3c7ecb..6a79013 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -1,35 +1,51 @@
-# Use an official Python runtime as a base image
-FROM python:3.6
+# Use an official pypy runtime as a base image
+FROM pypy:3
ENV INSROOT /opt/app
ENV APPUSER snmptrap
ENV APPDIR ${INSROOT}/${APPUSER}
+# add group and user: ubuntu
RUN useradd -d ${APPDIR} ${APPUSER}
+#
+# add group and user: ubuntu - for when DCAE platform evolves and runs as NON-ROOT!!!
+# RUN addgroup -g 1000 -S ${APPUSER} && \
+# adduser -u 1000 -S ${APPUSER} -G ${APPUSER}
WORKDIR ${APPDIR}
-EXPOSE 162:162/udp
+EXPOSE 162:6162/udp
# Copy the current directory contents into the container at ${APPDIR}
COPY ./snmptrap/ ./bin/
COPY ./etc/ ./etc/
COPY requirements.txt ./
-RUN pip install -r requirements.txt
+#
+# RUN pip install -r requirements.txt
+RUN pip install --trusted-host files.pythonhosted.org -r requirements.txt
+
+RUN mkdir -p /etc \
+ && mkdir -p /etc/apt
+RUN apt-get update -y && apt-get install -y jq bc vim
RUN mkdir -p ${APPDIR}/data \
&& mkdir -p ${APPDIR}/logs \
&& mkdir -p ${APPDIR}/tmp \
- && chown -R ${APPUSER}:${APPUSER} ${APPDIR} \
+# && chown -R ${APPUSER}:${APPUSER} ${APPDIR} \
&& chmod a+w ${APPDIR}/data \
&& chmod a+w ${APPDIR}/logs \
&& chmod a+w ${APPDIR}/tmp \
&& chmod 500 ${APPDIR}/etc \
- && chmod 500 ${APPDIR}/bin/snmptrapd.sh
+ && chmod 500 ${APPDIR}/bin/snmptrapd.sh \
+ && chmod 500 ${APPDIR}/bin/scheduler.sh \
+# && ln -s /usr/bin/python3 /usr/bin/python \
+ && rm ${APPDIR}/requirements.txt
-USER ${APPUSER}
+# run everything from here on as $APPUSER, NOT ROOT!
+#USER ${APPUSER}
+# map logs directory to external volume
VOLUME ${APPDIR}/logs
-# Run run_policy.sh when the container launches
+# launch container
CMD ["./bin/snmptrapd.sh", "start"]
diff --git a/LICENSE.txt b/LICENSE.txt
index 22d3915..fef50eb 100644
--- a/LICENSE.txt
+++ b/LICENSE.txt
@@ -1,7 +1,7 @@
============LICENSE_START=======================================================
org.onap.dcae
================================================================================
-Copyright (c) 2017 AT&T Intellectual Property. All rights reserved.
+Copyright (c) 2017-2020 AT&T Intellectual Property. All rights reserved.
================================================================================
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
diff --git a/etc/snmptrapd.json b/etc/snmptrapd.json
index 8281a35..cfaebe3 100644
--- a/etc/snmptrapd.json
+++ b/etc/snmptrapd.json
@@ -1,6 +1,6 @@
{
"snmptrapd": {
- "version": "1.4.0",
+ "version": "2.0",
"title": "ONAP SNMP Trap Receiver"
},
"protocols": {
@@ -11,12 +11,12 @@
"ipv6_port": 6162
},
"cache": {
- "dns_cache_ttl_seconds": 60
+ "dns_cache_ttl_seconds": 10800
},
"publisher": {
- "http_milliseconds_timeout": 1500,
- "http_retries": 3,
- "http_milliseconds_between_retries": 750,
+ "http_milliseconds_timeout": 500,
+ "http_retries": 2,
+ "http_milliseconds_between_retries": 250,
"http_primary_publisher": "true",
"http_peer_publisher": "unavailable",
"max_traps_between_publishes": 10,
@@ -49,9 +49,38 @@
"eelf_debug": "debug.log",
"eelf_audit": "audit.log",
"eelf_metrics": "metrics.log",
- "roll_frequency": "day",
+ "roll_frequency": "hour",
"minimum_severity_to_log": 3
},
+ "check_hb_traps": {
+ "trap_thr": 900,
+ "hb_thr": 900,
+ "hb_notify_oid": ".1.3.6.1.4.1.74.2.46.12.1.1"
+ },
+ "trap_config": {
+ "sw_interval_in_seconds": 60,
+ "metric_log_notification_threshold_pct": 25,
+ "notify_oids": [
+ {
+ "oid": ".1.3.6.1.4.1.9.0.1",
+ "sw_high_water_in_interval": 100,
+ "sw_low_water_in_interval": 5,
+ "category": "logonly"
+ },
+ {
+ "oid": ".1.3.6.1.4.1.9.0.2",
+ "sw_high_water_in_interval": 200,
+ "sw_low_water_in_interval": 10,
+ "category": "logonly"
+ },
+ {
+ "oid": ".1.3.6.1.4.1.9.0.3",
+ "sw_high_water_in_interval": 300,
+ "sw_low_water_in_interval": 15,
+ "category": "logonly"
+ }
+ ]
+ },
"snmpv3_config": {
"usm_users": [
{
diff --git a/etc/version.dat b/etc/version.dat
new file mode 100644
index 0000000..c1f0994
--- /dev/null
+++ b/etc/version.dat
@@ -0,0 +1 @@
+ONAP snmptrap v2.0.3 (built Wed Nov 20 11:48:38 EST 2019)
diff --git a/pom.xml b/pom.xml
index 53e143b..80c9eee 100644
--- a/pom.xml
+++ b/pom.xml
@@ -30,7 +30,7 @@ ECOMP is a trademark and service mark of AT&T Intellectual Property.
<groupId>org.onap.dcaegen2.collectors</groupId>
<artifactId>snmptrap</artifactId>
<name>dcaegen2-collectors-snmptrap</name>
- <version>1.4.0-SNAPSHOT</version>
+ <version>2.0.3-SNAPSHOT</version>
<url>http://maven.apache.org</url>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
diff --git a/setup.py b/setup.py
index dc0c44b..4ce8c88 100644
--- a/setup.py
+++ b/setup.py
@@ -27,12 +27,12 @@ from setuptools import setup, find_packages
setup(
name = "snmptrap",
description = "snmp trap receiver for ONAP docker image",
- version = "1.4.0",
+ version = "2.0.3",
packages=find_packages(),
install_requires=[
- "pysnmp==4.4.2",
- "requests==2.18.3",
- "onap_dcae_cbs_docker_client==1.0.1"
+ "pysnmp==4.4.2",
+ "requests==2.18.3",
+ "onap_dcae_cbs_docker_client==2.1.0"
],
author = "Dave L",
author_email = "dl3158@att.com",
diff --git a/snmptrap/__init__.py b/snmptrap/__init__.py
index 1875bf6..8b4cd11 100644
--- a/snmptrap/__init__.py
+++ b/snmptrap/__init__.py
@@ -1,5 +1,5 @@
-# ================================================================================
-# Copyright (c) 2018 AT&T Intellectual Property. All rights reserved.
+# ============LICENSE_START=======================================================
+# Copyright (c) 2017-2020 AT&T Intellectual Property. All rights reserved.
# ================================================================================
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
@@ -13,9 +13,6 @@
# See the License for the specific language governing permissions and
# limitations under the License.
# ============LICENSE_END=========================================================
-#
-# ECOMP is a trademark and service mark of AT&T Intellectual Property.
-
# empty __init__.py so that pytest can add correct path to coverage report, -- per pytest
# best practice guideline
diff --git a/snmptrap/healthcheck.sh b/snmptrap/healthcheck.sh
index 371c18a..8fe063b 100755
--- a/snmptrap/healthcheck.sh
+++ b/snmptrap/healthcheck.sh
@@ -1,11 +1,12 @@
-# ================================================================================
-# Copyright (c) 2018 AT&T Intellectual Property. All rights reserved.
+#!/bin/bash
+# ============LICENSE_START=======================================================
+# Copyright (c) 2018-2020 AT&T Intellectual Property. All rights reserved.
# ================================================================================
# 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
+# 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,
@@ -13,7 +14,6 @@
# See the License for the specific language governing permissions and
# limitations under the License.
# ============LICENSE_END=========================================================
-#!/bin/bash
# Health check script for service change handler
@@ -23,5 +23,3 @@
# run standard status command, exit with results
/opt/app/snmptrap/bin/snmptrapd.sh status > /dev/null 2>&1
-ret=$?
-exit ${ret}
diff --git a/snmptrap/mod/__init__.py b/snmptrap/mod/__init__.py
index 1875bf6..b0cfa95 100644
--- a/snmptrap/mod/__init__.py
+++ b/snmptrap/mod/__init__.py
@@ -1,5 +1,5 @@
-# ================================================================================
-# Copyright (c) 2018 AT&T Intellectual Property. All rights reserved.
+# ============LICENSE_START=======================================================
+# Copyright (c) 2018-2020 AT&T Intellectual Property. All rights reserved.
# ================================================================================
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
@@ -13,9 +13,6 @@
# See the License for the specific language governing permissions and
# limitations under the License.
# ============LICENSE_END=========================================================
-#
-# ECOMP is a trademark and service mark of AT&T Intellectual Property.
-
# empty __init__.py so that pytest can add correct path to coverage report, -- per pytest
# best practice guideline
diff --git a/snmptrap/mod/trapd_exit.py b/snmptrap/mod/trapd_exit.py
index ef7a2ae..6d1ea45 100644
--- a/snmptrap/mod/trapd_exit.py
+++ b/snmptrap/mod/trapd_exit.py
@@ -1,7 +1,5 @@
# ============LICENSE_START=======================================================
-# org.onap.dcae
-# ================================================================================
-# Copyright (c) 2017-2018 AT&T Intellectual Property. All rights reserved.
+# Copyright (c) 2017-2020 AT&T Intellectual Property. All rights reserved.
# ================================================================================
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
@@ -15,9 +13,6 @@
# See the License for the specific language governing permissions and
# limitations under the License.
# ============LICENSE_END=========================================================
-#
-# ECOMP is a trademark and service mark of AT&T Intellectual Property.
-#
"""
trapc_exit_snmptrapd is responsible for removing any existing runtime PID
file, and exiting with the provided (param 1) exit code
diff --git a/snmptrap/mod/trapd_get_cbs_config.py b/snmptrap/mod/trapd_get_cbs_config.py
index 8bdff15..272aabe 100644
--- a/snmptrap/mod/trapd_get_cbs_config.py
+++ b/snmptrap/mod/trapd_get_cbs_config.py
@@ -1,7 +1,5 @@
# ============LICENSE_START=======================================================
-# org.onap.dcae
-# ================================================================================
-# Copyright (c) 2018 AT&T Intellectual Property. All rights reserved.
+# Copyright (c) 2018-2020 AT&T Intellectual Property. All rights reserved.
# ================================================================================
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
@@ -15,9 +13,6 @@
# See the License for the specific language governing permissions and
# limitations under the License.
# ============LICENSE_END=========================================================
-#
-# ECOMP is a trademark and service mark of AT&T Intellectual Property.
-#
"""
Look for CBS broker and return application config; if not present, look for
env variable that specifies JSON equiv of CBS config (typically used for
@@ -74,47 +69,52 @@ def get_cbs_config():
except Exception as e:
msg = "CBS_SIM_JSON not defined - FATAL ERROR, exiting"
stdout_logger(msg)
- cleanup_and_exit(1,None)
+ cleanup_and_exit(1, None)
if _cbs_sim_json_file == "None":
msg = "CBS_SIM_JSON not defined - FATAL ERROR, exiting"
stdout_logger(msg)
- cleanup_and_exit(1,None)
+ cleanup_and_exit(1, None)
else:
msg = ("ONAP controller override specified via CBS_SIM_JSON: %s" %
_cbs_sim_json_file)
stdout_logger(msg)
try:
tds.c_config = json.load(open(_cbs_sim_json_file))
- msg = ("%s loaded and parsed successfully" % _cbs_sim_json_file)
+ msg = ("%s loaded and parsed successfully" %
+ _cbs_sim_json_file)
stdout_logger(msg)
except Exception as e:
msg = "Unable to load CBS_SIM_JSON " + _cbs_sim_json_file + \
" (invalid json?) - FATAL ERROR, exiting"
stdout_logger(msg)
- cleanup_and_exit(1,None)
+ cleanup_and_exit(1, None)
+
+ # display consul config returned, regardless of source
+ msg = "cbs config: %s" % json.dumps(tds.c_config)
+ stdout_logger(msg)
# recalc timeout, set default if not present
try:
- tds.timeout_seconds = tds.c_config['publisher']['http_milliseconds_timeout'] / 1000.0
+ tds.timeout_seconds = float(tds.c_config['publisher']['http_milliseconds_timeout'] / 1000.0)
except Exception as e:
- tds.timeout_seconds = 1.5
+ tds.timeout_seconds = float(1.5)
# recalc seconds_between_retries, set default if not present
try:
- tds.seconds_between_retries = tds.c_config['publisher']['http_milliseconds_between_retries'] / 1000.0
+ tds.seconds_between_retries = float(tds.c_config['publisher']['http_milliseconds_between_retries'] / 1000.0)
except Exception as e:
- tds.seconds_between_retries = .750
+ tds.seconds_between_retries = float(.750)
# recalc min_severity_to_log, set default if not present
try:
- tds.minimum_severity_to_log = tds.c_config['files']['minimum_severity_to_log']
+ tds.minimum_severity_to_log = int(tds.c_config['files']['minimum_severity_to_log'])
except Exception as e:
- tds.minimum_severity_to_log = 3
+ tds.minimum_severity_to_log = int(3)
try:
- tds.publisher_retries = tds.c_config['publisher']['http_retries']
+ tds.publisher_retries = int(tds.c_config['publisher']['http_retries'])
except Exception as e:
- tds.publisher_retries = 2
+ tds.publisher_retries = int(2)
return True
diff --git a/snmptrap/mod/trapd_http_session.py b/snmptrap/mod/trapd_http_session.py
index 3efca21..14abb21 100644
--- a/snmptrap/mod/trapd_http_session.py
+++ b/snmptrap/mod/trapd_http_session.py
@@ -1,7 +1,5 @@
# ============LICENSE_START=======================================================
-# org.onap.dcae
-# ================================================================================
-# Copyright (c) 2017-2018 AT&T Intellectual Property. All rights reserved.
+# Copyright (c) 2017-2020 AT&T Intellectual Property. All rights reserved.
# ================================================================================
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
@@ -15,9 +13,6 @@
# See the License for the specific language governing permissions and
# limitations under the License.
# ============LICENSE_END=========================================================
-#
-# ECOMP is a trademark and service mark of AT&T Intellectual Property.
-#
"""
trapd_http_session establishes an http session for future use in publishing
messages to the dmaap cluster.
@@ -79,7 +74,6 @@ def close_session_obj(_loc_http_requ_session):
none
"""
-
# Close existing session if present.
if _loc_http_requ_session is not None:
try:
@@ -87,7 +81,8 @@ def close_session_obj(_loc_http_requ_session):
return True
except Exception as e:
msg = "Unable to close current http session - FATAL ERROR, exiting"
- ecomp_logger(tds.LOG_TYPE_ERROR, tds.SEV_FATAL, tds.CODE_GENERAL, msg)
+ ecomp_logger(tds.LOG_TYPE_ERROR, tds.SEV_FATAL,
+ tds.CODE_GENERAL, msg)
stdout_logger(msg)
cleanup_and_exit(1, tds.pid_file_name)
@@ -111,7 +106,6 @@ def reset_session_obj(_loc_http_requ_session):
none
"""
-
# close existing http_requ_session if present
ret = close_session_obj(_loc_http_requ_session)
diff --git a/snmptrap/mod/trapd_io.py b/snmptrap/mod/trapd_io.py
index d079cbe..991bcbd 100644
--- a/snmptrap/mod/trapd_io.py
+++ b/snmptrap/mod/trapd_io.py
@@ -1,7 +1,5 @@
-# ============LICENSE_START=======================================================)
-# org.onap.dcae
-# ================================================================================
-# Copyright (c) 2018 AT&T Intellectual Property. All rights reserved.
+# ============LICENSE_START=======================================================
+# Copyright (c) 2018-2020 AT&T Intellectual Property. All rights reserved.
# ================================================================================
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
@@ -15,9 +13,6 @@
# See the License for the specific language governing permissions and
# limitations under the License.
# ============LICENSE_END=========================================================
-#
-# ECOMP is a trademark and service mark of AT&T Intellectual Property.
-#
"""
"""
@@ -180,7 +175,7 @@ def roll_file(_loc_file_name):
_msg = ("ERROR: Unable to rename %s to %s"
% (_loc_file_name,
_loc_file_name_bak))
- ecomp_logger(tds.LOG_TYPE_ERROR, tds.SEV_CRIT,
+ ecomp_logger(tds.LOG_TYPE_ERROR, tds.SEV_ERROR,
tds.CODE_GENERAL, _msg)
return False
@@ -231,6 +226,7 @@ def close_file(_loc_fd, _loc_filename):
# is released for python via LOG-161
# # # # # # # # # # ## # # # # # # #
+
def ecomp_logger(_log_type, _sev, _error_code, _msg):
"""
Log to ecomp-style logfiles. Logs include:
@@ -309,10 +305,10 @@ def ecomp_logger(_log_type, _sev, _error_code, _msg):
# above were various attempts at setting time string found in other
# libs; instead, let's keep it real:
- t_out = datetime.datetime.utcnow().strftime("%Y-%m-%dT%H:%M:%S,%f")[:-3]
+ t_out = datetime.datetime.utcnow().strftime("%Y-%m-%dT%H:%M:%S.%f")[:-3]
calling_fx = inspect.stack()[1][3]
- # DLFM: this entire module is a hack to override concept of prog logging
+ # DLFM: this entire module is a xyz to override concept of prog logging
# written across multiple files (???), making diagnostics IMPOSSIBLE!
# Hoping to leverage ONAP logging libraries & standards when available
@@ -331,14 +327,19 @@ def ecomp_logger(_log_type, _sev, _error_code, _msg):
if _sev >= tds.minimum_severity_to_log:
# log to appropriate eelf log (different files ??)
if _log_type == tds.LOG_TYPE_ERROR:
- _out_rec = ('%s|%s|%s|%s|%s|%s|%s|%s|%s'
- % (calling_fx, "snmptrapd", unused, unused, unused, tds.SEV_TYPES[_sev], _error_code, unused, _msg))
+ # _out_rec = ('%s|%s|%s|%s|%s|%s|%s|%s|%s'
+ # _out_rec = ('%s|%s|%s|%s|%s|%s|%s|%s|%s'
+ # % (calling_fx, "snmptrapd", unused, unused, unused, tds.SEV_TYPES[_sev], _error_code, unused, _msg))
+ _out_rec = ('%s|%s|%s|%s|%s|%s|%s|%s|%s|%s|%s|%s|%s|%s|%s|%s|%s|%s|%s|%s|%s|%s|%s|%s|%s|%s|%s'
+ % (unused, unused, calling_fx, unused, "snmptrapd", unused, unused, unused, unused, unused, unused, unused, tds.SEV_TYPES[_sev], unused, unused, unused, unused, unused, unused, unused, unused, unused, unused, unused, unused, unused, _msg))
try:
- tds.eelf_error_fd.write('%s|%s\n' % (t_out, str(_out_rec)))
+ tds.eelf_error_fd.write('%s|%s|%s\n' % (t_out, t_out, str(_out_rec)))
except Exception as e:
stdout_logger(str(_out_rec))
elif _log_type == tds.LOG_TYPE_AUDIT:
# log message in AUDIT format
+ # _out_rec = ('%s|%s|%s|%s|%s|%s|%s|%s|%s'
+ # % (calling_fx, "snmptrapd", unused, unused, unused, tds.SEV_TYPES[_sev], _error_code, unused, _msg))
_out_rec = ('%s|%s|%s|%s|%s|%s|%s|%s|%s'
% (calling_fx, "snmptrapd", unused, unused, unused, tds.SEV_TYPES[_sev], _error_code, unused, _msg))
try:
@@ -358,8 +359,8 @@ def ecomp_logger(_log_type, _sev, _error_code, _msg):
# DLFM: too much I/O !!!
# always write to debug; we need ONE logfile that has time-sequence full view !!!
# log message in DEBUG format
- _out_rec = ("%s|%s|%s|%s|%s|%s|%s|%s|%s"
- % (calling_fx, "snmptrapd", unused, unused, unused, tds.SEV_TYPES[_sev], _error_code, unused, _msg))
+ _out_rec = ("%s|%s|%s|%s|%s|%s|%s|%s|%s|%s"
+ % (unused, calling_fx, "snmptrapd", unused, unused, unused, tds.SEV_TYPES[_sev], _error_code, unused, _msg))
try:
tds.eelf_debug_fd.write('%s|%s\n' % (t_out, str(_out_rec)))
except Exception as e:
diff --git a/snmptrap/mod/trapd_runtime_pid.py b/snmptrap/mod/trapd_runtime_pid.py
index c6ef76e..74668f5 100644
--- a/snmptrap/mod/trapd_runtime_pid.py
+++ b/snmptrap/mod/trapd_runtime_pid.py
@@ -1,7 +1,5 @@
# ============LICENSE_START=======================================================
-# org.onap.dcae
-# ================================================================================
-# Copyright (c) 2017-2018 AT&T Intellectual Property. All rights reserved.
+# Copyright (c) 2017-2020 AT&T Intellectual Property. All rights reserved.
# ================================================================================
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
@@ -15,9 +13,6 @@
# See the License for the specific language governing permissions and
# limitations under the License.
# ============LICENSE_END=========================================================
-#
-# ECOMP is a trademark and service mark of AT&T Intellectual Property.
-#
"""
trapd_runtime_pid maintains a 'PID file' (file that contains the
PID of currently running trap receiver)
diff --git a/snmptrap/mod/trapd_settings.py b/snmptrap/mod/trapd_settings.py
index 308a2f2..41dc18f 100644
--- a/snmptrap/mod/trapd_settings.py
+++ b/snmptrap/mod/trapd_settings.py
@@ -1,7 +1,5 @@
-# ============LICENSE_START=======================================================)
-# org.onap.dcae
-# ================================================================================
-# Copyright (c) 2018 AT&T Intellectual Property. All rights reserved.
+# ============LICENSE_START=======================================================
+# Copyright (c) 2018-2020 AT&T Intellectual Property. All rights reserved.
# ================================================================================
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
@@ -15,9 +13,6 @@
# See the License for the specific language governing permissions and
# limitations under the License.
# ============LICENSE_END=========================================================
-#
-# ECOMP is a trademark and service mark of AT&T Intellectual Property.
-#
"""
"""
@@ -81,10 +76,12 @@ def init():
first_varbind = True
global trap_dict
trap_dict = {}
- global all_traps_str
- all_traps_str = ""
+ global all_traps_json_str
+ all_traps_json_str = ""
global all_vb_json_str
all_vb_json_str = ""
+ global all_vb_str
+ all_vb_str = ""
global trap_uuids_in_buffer
trap_uuids_in_buffer = ""
# </trap and varbind dictionaries>
@@ -138,8 +135,6 @@ def init():
global sw_count_dict
sw_count_dict = {}
- global sw_interval_in_seconds
- sw_interval_in_seconds = 60
# </stormwatch >
# <logging types and severities>
@@ -161,14 +156,14 @@ def init():
global SEV_DETAILED
global SEV_INFO
global SEV_WARN
- global SEV_CRIT
+ global SEV_ERROR
global SEV_FATAL
- SEV_TYPES = ["none", "DETAILED", "INFO", "WARN", "CRITICAL", "FATAL"]
+ SEV_TYPES = ["none", "DETAILED", "INFO", "WARN", "ERROR", "FATAL"]
SEV_NONE = 0
SEV_DETAILED = 1
SEV_INFO = 2
SEV_WARN = 3
- SEV_CRIT = 4
+ SEV_ERROR = 4
SEV_FATAL = 5
global CODE_GENERAL
diff --git a/snmptrap/mod/trapd_snmpv3.py b/snmptrap/mod/trapd_snmpv3.py
index 5c0382b..b421ae1 100644
--- a/snmptrap/mod/trapd_snmpv3.py
+++ b/snmptrap/mod/trapd_snmpv3.py
@@ -1,7 +1,5 @@
# ============LICENSE_START=======================================================
-# org.onap.dcae
-# ================================================================================
-# Copyright (c) 2018 AT&T Intellectual Property. All rights reserved.
+# Copyright (c) 2018-2020 AT&T Intellectual Property. All rights reserved.
# ================================================================================
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
@@ -15,9 +13,6 @@
# See the License for the specific language governing permissions and
# limitations under the License.
# ============LICENSE_END=========================================================
-#
-# ECOMP is a trademark and service mark of AT&T Intellectual Property.
-#
"""
module for snmpv3 support
@@ -52,9 +47,9 @@ prog_name = os.path.basename(__file__)
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
-def load_snmpv3_credentials (_py_config, _snmp_engine, _cbs_config):
+def load_snmpv3_credentials(_py_config, _snmp_engine, _cbs_config):
"""
- Add V3 credentials from CBS config to receiver config
+ Add V3 credentials from CBS config to receiver config
so traps will be recieved from specified engines/users
:Parameters:
_config: snmp entity config
@@ -63,7 +58,7 @@ def load_snmpv3_credentials (_py_config, _snmp_engine, _cbs_config):
# add V3 credentials from CBS json structure to running config
try:
- v3_users=_cbs_config["snmpv3_config"]["usm_users"]
+ v3_users = _cbs_config["snmpv3_config"]["usm_users"]
except Exception as e:
msg = ("No V3 users defined")
ecomp_logger(tds.LOG_TYPE_DEBUG, tds.SEV_INFO, tds.CODE_GENERAL, msg)
@@ -73,121 +68,126 @@ def load_snmpv3_credentials (_py_config, _snmp_engine, _cbs_config):
# engineId
try:
- ctx_engine_id=v3_user['engineId']
+ ctx_engine_id = v3_user['engineId']
except Exception as e:
- ctx_engine_id=None
+ ctx_engine_id = None
# user
try:
- userName=v3_user['user']
+ userName = v3_user['user']
except Exception as e:
- userName=None
+ userName = None
# authorization
# find options at -> site-packages/pysnmp/entity/config.py
- # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
+ # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# print("Checking auth for %s" % (userName))
# usmHMACMD5AuthProtocol
try:
- authKey=v3_user['usmHMACMD5AuthProtocol']
- authProtocol=config.usmHMACMD5AuthProtocol
+ authKey = v3_user['usmHMACMD5AuthProtocol']
+ authProtocol = config.usmHMACMD5AuthProtocol
except Exception as e:
try:
- authKey=v3_user['usmHMACSHAAuthProtocol']
- authProtocol=config.usmHMACSHAAuthProtocol
+ authKey = v3_user['usmHMACSHAAuthProtocol']
+ authProtocol = config.usmHMACSHAAuthProtocol
except Exception as e:
try:
- authKey=v3_user['usmHMAC128SHA224AuthProtocol']
- authProtocol=config.usmHMAC128SHA224AuthProtocol
+ authKey = v3_user['usmHMAC128SHA224AuthProtocol']
+ authProtocol = config.usmHMAC128SHA224AuthProtocol
except Exception as e:
try:
- authKey=v3_user['usmHMAC192SHA256AuthProtocol']
- authProtocol=config.usmHMAC192SHA256AuthProtocol
+ authKey = v3_user['usmHMAC192SHA256AuthProtocol']
+ authProtocol = config.usmHMAC192SHA256AuthProtocol
except Exception as e:
try:
- authKey=v3_user['usmHMAC256SHA384AuthProtocol']
- authProtocol=config.usmHMAC256SHA384AuthProtocol
+ authKey = v3_user['usmHMAC256SHA384AuthProtocol']
+ authProtocol = config.usmHMAC256SHA384AuthProtocol
except Exception as e:
try:
- authKey=v3_user['usmHMAC384SHA512AuthProtocol']
- authProtocol=config.usmHMAC384SHA512AuthProtocol
+ authKey = v3_user['usmHMAC384SHA512AuthProtocol']
+ authProtocol = config.usmHMAC384SHA512AuthProtocol
except Exception as e:
try:
- authKey=v3_user['usmNoAuthProtocol']
- authProtocol=config.usmNoAuthProtocol
+ authKey = v3_user['usmNoAuthProtocol']
+ authProtocol = config.usmNoAuthProtocol
except Exception as e:
# FMDL: default to NoAuth, or error/skip entry?
- msg = ("No auth specified for user %s ?" % (userName))
- authKey=None
- authProtocol=config.usmNoAuthProtocol
- ecomp_logger(tds.LOG_TYPE_DEBUG, tds.SEV_INFO, tds.CODE_GENERAL, msg)
+ msg = (
+ "No auth specified for user %s ?" % (userName))
+ authKey = None
+ authProtocol = config.usmNoAuthProtocol
+ ecomp_logger(
+ tds.LOG_TYPE_DEBUG, tds.SEV_INFO, tds.CODE_GENERAL, msg)
# privacy
# find options at -> site-packages/pysnmp/entity/config.py
- # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
+ # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# print("Checking priv for %s" % (userName))
# usm3DESEDEPriv
try:
- privKey=v3_user['usm3DESEDEPrivProtocol']
- privProtocol=config.usm3DESEDEPrivProtocol
+ privKey = v3_user['usm3DESEDEPrivProtocol']
+ privProtocol = config.usm3DESEDEPrivProtocol
except Exception as e:
# usmAesCfb128Protocol
try:
- privKey=v3_user['usmAesCfb128Protocol']
- privProtocol=config.usmAesCfb128Protocol
+ privKey = v3_user['usmAesCfb128Protocol']
+ privProtocol = config.usmAesCfb128Protocol
except Exception as e:
# usmAesCfb192Protocol
try:
- privKey=v3_user['usmAesCfb192Protocol']
- privProtocol=config.usmAesCfb192Protocol
+ privKey = v3_user['usmAesCfb192Protocol']
+ privProtocol = config.usmAesCfb192Protocol
except Exception as e:
# usmAesBlumenthalCfb192Protocol
try:
- privKey=v3_user['usmAesBlumenthalCfb192Protocol']
- privProtocol=config.usmAesBlumenthalCfb192Protocol
+ privKey = v3_user['usmAesBlumenthalCfb192Protocol']
+ privProtocol = config.usmAesBlumenthalCfb192Protocol
except Exception as e:
# usmAesCfb256Protocol
try:
- privKey=v3_user['usmAesCfb256Protocol']
- privProtocol=config.usmAesCfb256Protocol
+ privKey = v3_user['usmAesCfb256Protocol']
+ privProtocol = config.usmAesCfb256Protocol
except Exception as e:
# usmAesBlumenthalCfb256Protocol
try:
- privKey=v3_user['usmAesBlumenthalCfb256Protocol']
- privProtocol=config.usmAesBlumenthalCfb256Protocol
+ privKey = v3_user['usmAesBlumenthalCfb256Protocol']
+ privProtocol = config.usmAesBlumenthalCfb256Protocol
except Exception as e:
# usmDESPrivProtocol
try:
- privKey=v3_user['usmDESPrivProtocol']
- privProtocol=config.usmDESPrivProtocol
+ privKey = v3_user['usmDESPrivProtocol']
+ privProtocol = config.usmDESPrivProtocol
except Exception as e:
# usmNoPrivProtocol
try:
- privKey=v3_user['usmNoPrivProtocol']
- privProtocol=config.usmNoPrivProtocol
+ privKey = v3_user['usmNoPrivProtocol']
+ privProtocol = config.usmNoPrivProtocol
except Exception as e:
# FMDL: default to NoPriv, or error/skip entry?
- msg = ("No priv specified for user %s" % (userName))
- ecomp_logger(tds.LOG_TYPE_DEBUG, tds.SEV_INFO, tds.CODE_GENERAL, msg)
- privKey=None
- privProtocol=config.usmNoPrivProtocol
+ msg = (
+ "No priv specified for user %s" % (userName))
+ ecomp_logger(
+ tds.LOG_TYPE_DEBUG, tds.SEV_INFO, tds.CODE_GENERAL, msg)
+ privKey = None
+ privProtocol = config.usmNoPrivProtocol
# break
# msg = ("userName: %s authKey: %s authProtocol: %s privKey: %s privProtocol: %s engineId: %s % (userName, authKey, authProtocol, privKey, privProtocol, ctx_engine_id))
- msg = ("userName: %s authKey: **** authProtocol: %s privKey: **** privProtocol: %s engineId: ****" % (userName, authProtocol, privProtocol))
+ msg = ("userName: %s authKey: **** authProtocol: %s privKey: **** privProtocol: %s engineId: ****" %
+ (userName, authProtocol, privProtocol))
ecomp_logger(tds.LOG_TYPE_DEBUG, tds.SEV_INFO, tds.CODE_GENERAL, msg)
# user: usr-md5-des, auth: MD5, priv DES, contextEngineId: 8000000001020304
# this USM entry is used for TRAP receiving purposes
- # help(addV3User) returns ->
+ # help(addV3User) returns ->
# addV3User(snmpEngine, userName, authProtocol=(1, 3, 6, 1, 6, 3, 10, 1, 1, 1), authKey=None, privProtocol=(1, 3, 6, 1, 6, 3, 10, 1, 2, 1), priv Key=None, securityEngineId=None, securityName=None, contextEngineId=None)
- if ctx_engine_id is not None:
+ if ctx_engine_id is not None:
config.addV3User(
_snmp_engine, userName,
authProtocol, authKey,
diff --git a/snmptrap/mod/trapd_stats_settings.py b/snmptrap/mod/trapd_stats_settings.py
new file mode 100644
index 0000000..df4da0e
--- /dev/null
+++ b/snmptrap/mod/trapd_stats_settings.py
@@ -0,0 +1,44 @@
+# ============LICENSE_START=======================================================
+# Copyright (c) 2020 AT&T Intellectual Property. All rights reserved.
+# ================================================================================
+# 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.
+# ============LICENSE_END=========================================================
+"""
+"""
+
+__docformat__ = 'restructuredtext'
+
+
+def init():
+
+ # <stats>
+ #
+ # oid_counter_dict
+ # key [<notify oid>] -> count
+ #
+ # agent_counter_dict
+ # key [<agent>] -> count
+ #
+ global oid_counter_dict
+ oid_counter_dict = {}
+
+ global agent_counter_dict
+ agent_counter_dict = {}
+
+ global total_notifications
+ total_notifications = 0
+
+ global metric_log_notification_threshold_pct
+ metric_log_notification_threshold_pct = 25
+
+ # </stats>
diff --git a/snmptrap/mod/trapd_stormwatch.py b/snmptrap/mod/trapd_stormwatch.py
new file mode 100644
index 0000000..623fe39
--- /dev/null
+++ b/snmptrap/mod/trapd_stormwatch.py
@@ -0,0 +1,513 @@
+# ============LICENSE_START=======================================================
+# Copyright (c) 2020 AT&T Intellectual Property. All rights reserved.
+# ================================================================================
+# 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.
+# ============LICENSE_END=========================================================
+"""
+trapd_stormwatch makes the decision on whether an
+arriving SNMP trap is exceeding a pre-configured
+threshold (storm), and if so it will return "False"
+so the trap can be logged and immediately discarded
+"""
+
+__docformat__ = 'restructuredtext'
+
+import sys
+import os
+import string
+import time
+
+from trapd_io import stdout_logger, ecomp_logger
+import trapd_settings as tds
+import trapd_stats_settings as stats
+import trapd_stormwatch_settings as sws
+from trapd_exit import cleanup_and_exit
+
+prog_name = os.path.basename(__file__)
+
+
+def sw_init():
+
+ # <Storm Watch>
+ #
+ # sw_storm_counter_dict
+ # key [<ip address>.<notify oid>] -> count
+ #
+ # sw_storm_active_dict
+ # key [<ip address>.<notify oid>] -> True (or False, but no key present
+ # means False)
+ #
+ # sw_config_oid_dict
+ # key [<notify oid>] -> "true" (key presence means it participates)
+ #
+ # sw_config_low_water_in_interval_dict
+ # key [<notify oid>] -> <int> value that stormActive turns False
+ #
+ # sw_config_high_water_in_interval_dict
+ # key [<notify oid>] -> <int> value that stormActive turns True
+ #
+ sws.sw_counter_dict = {}
+ sws.sw_storm_active_dict = {}
+ sws.sw_config_oid_dict = {}
+ sws.sw_config_low_water_in_interval_dict = {}
+ sws.sw_config_high_water_in_interval_dict = {}
+ sws.sw_interval_in_seconds = 60
+
+
+# # # # # # # # # # # # #
+# fx: sw_storm_active
+# - check if storm is active for agent/oid
+# - returns True if yes, False if no
+# # # # # # # # # # # # #
+def sw_clear_dicts():
+ """
+ Clear all storm watch dictionaries
+ :Parameters:
+ :Exceptions:
+ none
+ :Keywords:
+ stormwatch count threshold
+ :Variables:
+ """
+ try:
+ stats.oid_counter_dict.clear()
+ stats.agent_counter_dict.clear()
+ sws.sw_storm_active_dict.clear()
+ sws.sw_storm_counter_dict.clear()
+ sws.sw_config_oid_dict.clear()
+ sws.sw_config_low_water_in_interval_dict.clear()
+ sws.sw_config_high_water_in_interval_dict.clear()
+ sws.sw_config_category.clear()
+ return True
+ except Exception as e:
+ msg = "unable to reset stormwatch dictionaries - results will be indeterminate: %s" % (
+ e)
+ ecomp_logger(tds.LOG_TYPE_ERROR, tds.SEV_WARN, tds.CODE_GENERAL, msg)
+ return False
+
+# # # # # # # # # # # # #
+# fx: sw_load_trap_config
+# - load trap configurations from CBS response
+# # # # # # # # # # # # #
+
+
+def sw_load_trap_config(_config):
+ """
+ Load trap configs into dictionary
+ :Parameters:
+ _config: trapd_config from CBS
+ :Exceptions:
+ """
+
+ # clear any dicts present from previous invocations
+ try:
+ sws.sw_storm_active_dict
+ ret = sw_clear_dicts()
+ msg = ("reset existing sws dictionaries to empty")
+ ecomp_logger(tds.LOG_TYPE_DEBUG, tds.SEV_INFO, tds.CODE_GENERAL, msg)
+ except NameError:
+ msg = ("sws dictionaries not present - initializing")
+ ecomp_logger(tds.LOG_TYPE_DEBUG, tds.SEV_INFO, tds.CODE_GENERAL, msg)
+ ret = sw_init()
+
+ # set last storm analysis to now
+ sws.sw_last_stormwatch_dict_analysis = int(time.time())
+
+ # get metric % threshold for logging trap count by-agent to metric log
+ try:
+ stats.metric_log_notification_threshold_pct = int(
+ _config["trap_config"]["metric_log_notification_threshold_pct"])
+ msg = ("metric_log_notification_threshold_pct value: %d" %
+ stats.metric_log_notification_threshold_pct)
+ ecomp_logger(tds.LOG_TYPE_DEBUG, tds.SEV_INFO, tds.CODE_GENERAL, msg)
+ except Exception as e:
+ msg = (
+ "metric_log_notification_threshold_pct not present in config - default to 25")
+ ecomp_logger(tds.LOG_TYPE_DEBUG, tds.SEV_WARN, tds.CODE_GENERAL, msg)
+ stats.metric_log_notification_threshold_pct = 25
+
+ # get stormwatch interval; default to 60 seconds
+ try:
+ sws.sw_interval_in_seconds = int(
+ _config["trap_config"]["sw_interval_in_seconds"])
+ msg = ("sw_interval_in_seconds value: %d" % sws.sw_interval_in_seconds)
+ ecomp_logger(tds.LOG_TYPE_DEBUG, tds.SEV_INFO, tds.CODE_GENERAL, msg)
+ except Exception as e:
+ msg = ("sw_interval_in_seconds not present in config - default to 60 seconds")
+ ecomp_logger(tds.LOG_TYPE_DEBUG, tds.SEV_WARN, tds.CODE_GENERAL, msg)
+ sws.sw_interval_in_seconds = 60
+
+ # add trap configs from CBS json structure to running config
+ try:
+ notify_oids = _config["trap_config"]["notify_oids"]
+ except Exception as e:
+ msg = ("no trap_config or notify_oids defined")
+ ecomp_logger(tds.LOG_TYPE_DEBUG, tds.SEV_WARN, tds.CODE_GENERAL, msg)
+ return False
+
+ trap_block_counter = 0
+ for trap_block in notify_oids:
+ # oid
+ try:
+ _oid = trap_block['oid']
+ except Exception as e:
+ msg = (
+ "missing oid value in notify_oids - oid section of CBS config - using empty value, disregard entry")
+ ecomp_logger(tds.LOG_TYPE_DEBUG, tds.SEV_WARN,
+ tds.CODE_GENERAL, msg)
+ _oid = None
+
+ # sw_high_water_in_interval
+ try:
+ _sw_high_water_in_interval = int(
+ trap_block['sw_high_water_in_interval'])
+ except Exception as e:
+ msg = (
+ "missing sw_high_water_in_interval value in notify_oids - oid section of CBS config - using empty value")
+ ecomp_logger(tds.LOG_TYPE_DEBUG, tds.SEV_WARN,
+ tds.CODE_GENERAL, msg)
+ _sw_high_water_in_interval = None
+
+ # sw_low_water_in_interval
+ try:
+ _sw_low_water_in_interval = int(
+ trap_block['sw_low_water_in_interval'])
+ except Exception as e:
+ msg = (
+ "missing sw_low_water_in_interval value in notify_oids - oid section of CBS config - using empty value")
+ ecomp_logger(tds.LOG_TYPE_DEBUG, tds.SEV_WARN,
+ tds.CODE_GENERAL, msg)
+ _sw_low_water_in_interval = None
+
+ # category
+ try:
+ _category = trap_block['category']
+ except Exception as e:
+ msg = (
+ "missing category value in notify_oids - oid section of CBS config - using empty value")
+ ecomp_logger(tds.LOG_TYPE_DEBUG, tds.SEV_WARN,
+ tds.CODE_GENERAL, msg)
+ _category = None
+
+ if (_oid is not None and _category is not None and
+ _sw_low_water_in_interval is not None and _sw_high_water_in_interval is not None and
+ _sw_low_water_in_interval < _sw_high_water_in_interval):
+ # FMDL: Do we actually need sw_config_oid_dict?
+ msg = ("oid: %s sw_high_water_in_interval: %d sw_low_water_in_interval: %d category: %s" % (
+ _oid, _sw_high_water_in_interval, _sw_low_water_in_interval, _category))
+ ecomp_logger(tds.LOG_TYPE_DEBUG, tds.SEV_INFO,
+ tds.CODE_GENERAL, msg)
+ sws.sw_config_oid_dict[_oid] = True
+ sws.sw_config_low_water_in_interval_dict[_oid] = _sw_low_water_in_interval
+ sws.sw_config_high_water_in_interval_dict[_oid] = _sw_high_water_in_interval
+ sws.sw_config_category[_oid] = _category
+ trap_block_counter += 1
+ else:
+ msg = ("Missing or incorrect value for stormwatch config entry %d: skipping: %s" % (
+ trap_block_counter, trap_block))
+ ecomp_logger(tds.LOG_TYPE_DEBUG, tds.SEV_INFO,
+ tds.CODE_GENERAL, msg)
+
+
+ return trap_block_counter
+
+
+# # # # # # # # # # # # #
+# fx: sw_log_metrics
+# - log stats for any count in interval that is > sw_metric_log_notification_threshold_pct % of total arriving traps
+# # # # # # # # # # # # #
+def sw_log_metrics():
+ """
+ Log counts for agents that exceed sw_metric_log_notification_threshold_pct % of
+ total traps that arrived in interval
+ :Parameters:
+ :Exceptions:
+ none
+ :Keywords:
+ stormwatch metrics
+ :Variables:
+ """
+
+ msg = "total notifications: %d, interval in seconds: %d" % (
+ stats.total_notifications, sws.sw_interval_in_seconds)
+ ecomp_logger(tds.LOG_TYPE_METRICS, tds.SEV_INFO, tds.CODE_GENERAL, msg)
+
+ # print metrics for total traps and traps-per-second avg
+ # during sample interval
+ avg_traps_per_second = stats.total_notifications / 60
+ msg = "total traps: %d, interval in seconds: %d, average traps-per-second: %d" % (
+ stats.total_notifications, sws.sw_interval_in_seconds, avg_traps_per_second)
+ ecomp_logger(tds.LOG_TYPE_METRICS, tds.SEV_WARN,
+ tds.CODE_GENERAL, msg)
+
+ # print metrics for any agent that represents more than stats.metric_log_notification_threshold_pct
+ # during sample interval
+ for k in stats.agent_counter_dict:
+ c = stats.agent_counter_dict[k]
+ p = c / stats.total_notifications * 100
+ if p > stats.metric_log_notification_threshold_pct:
+ msg = "agent: %s, notifications: %d, interval in seconds: %d, percent of total traps: %d" % (
+ k, c, sws.sw_interval_in_seconds, p)
+ ecomp_logger(tds.LOG_TYPE_METRICS, tds.SEV_WARN,
+ tds.CODE_GENERAL, msg)
+
+# # # # # # # # # # # # #
+# fx: stats_increment_counters
+# - increment dictionary counters that are accumulating
+# - total traps by OID and agent for each sample interval
+# # # # # # # # # # # # #
+
+
+def stats_increment_counters(_loc_agent, _loc_oid):
+ """
+ update counters tracking traps-per-interval by
+ OID and agent
+ :Parameters:
+ _loc_agent
+ agent address from trap PDU
+ _loc_oid
+ notify OID from trap PDU
+ :Exceptions:
+ none
+ :Keywords:
+ stormwatch stats metrics
+ :Variables:
+ """
+ # increment oid occurances in window
+ msg = "increment metric counters for %s %s" % (_loc_agent, _loc_oid)
+ ecomp_logger(tds.LOG_TYPE_DEBUG, tds.SEV_INFO, tds.CODE_GENERAL, msg)
+ try:
+ stats.total_notifications += 1
+ except Exception as e:
+ stats.total_notifications = 1
+
+ try:
+ stats.oid_counter_dict[_loc_oid] += 1
+ except Exception as e:
+ stats.oid_counter_dict[_loc_oid] = 1
+
+ # increment agent occurances in window
+ try:
+ stats.agent_counter_dict[_loc_agent] += 1
+ except Exception as e:
+ stats.agent_counter_dict[_loc_agent] = 1
+
+
+# # # # # # # # # # # # #
+# fx: sw_storm_active
+# - check if storm is active for agent/oid
+# - returns True if yes, False if no
+# # # # # # # # # # # # #
+def sw_storm_active(_loc_agent, _loc_oid):
+ """
+ Check if this event is currently in an
+ active storm state.
+ :Parameters:
+ _loc_agent
+ agent address from trap PDU
+ _loc_oid
+ notify OID from trap PDU
+ :Exceptions:
+ none
+ :Keywords:
+ stormwatch count threshold
+ :Variables:
+ """
+
+ # we have to come here for every arriving trap, so increment
+ # trap counter dictionaries while we are here
+ stats_increment_counters(_loc_agent, _loc_oid)
+
+ # if we are at or above stormwatch interval, re-eval and re-set
+ elapsed_time = int(time.time()) - sws.sw_last_stormwatch_dict_analysis
+ if elapsed_time >= sws.sw_interval_in_seconds:
+ msg = "%d seconds has elapsed since stormwatch dictionary eval (%d second threshold) - check and reset counters " % (
+ elapsed_time, sws.sw_interval_in_seconds)
+ ecomp_logger(tds.LOG_TYPE_DEBUG, tds.SEV_INFO, tds.CODE_GENERAL, msg)
+ sw_log_metrics()
+ sw_reset_counter_dict()
+
+ # Note: If there's a sw_config_high_water_in_interval config value present
+ # that means it's participating in stormwatch, otherwise bail out
+ try:
+ _high_water_val = sws.sw_config_high_water_in_interval_dict[_loc_oid]
+ msg = "%s present in stormwatch config - high water value: %d" % (
+ _loc_oid, _high_water_val)
+ ecomp_logger(tds.LOG_TYPE_DEBUG, tds.SEV_INFO, tds.CODE_GENERAL, msg)
+ except Exception as e:
+ return False
+
+ # build stormwatch dict key by appending agent IP and trap oid
+ _dict_key = _loc_agent + " " + _loc_oid
+
+ # increment traps encountered for agent/oid
+ sw_increment_counter(_dict_key)
+
+ # first check if storm is active for _dict_key (early bail-out if so)
+ msg = "check if stormWatch is active for %s" % (_dict_key)
+ ecomp_logger(tds.LOG_TYPE_DEBUG, tds.SEV_INFO, tds.CODE_GENERAL, msg)
+ if sws.sw_storm_active_dict.get(_dict_key) is not None:
+ msg = "stormWatch is active for %s - return true" % (_dict_key)
+ ecomp_logger(tds.LOG_TYPE_DEBUG, tds.SEV_INFO, tds.CODE_GENERAL, msg)
+ return True
+ else:
+ msg = "no stormWatch active entry for %s - continue" % (_dict_key)
+ ecomp_logger(tds.LOG_TYPE_DEBUG, tds.SEV_INFO, tds.CODE_GENERAL, msg)
+
+ # if we got this far, trap is in stormwatch configs, we've incremented
+ # counter in sw_storm_counter_dict - figure out if we are over limit
+ if sws.sw_storm_counter_dict[_dict_key] > _high_water_val:
+ _loc_agent = _dict_key.split()[0]
+ _loc_oid = _dict_key.split()[1]
+ msg = "STORM ACTIVE: received %d events (%s) from %s (greater than high water threshold: %d)" % (
+ sws.sw_storm_counter_dict[_dict_key], _loc_oid, _loc_agent, _high_water_val)
+ ecomp_logger(tds.LOG_TYPE_AUDIT, tds.SEV_WARN, tds.CODE_GENERAL, msg)
+ try:
+ sws.sw_storm_active_dict[_dict_key] = True
+ except Exception as e:
+ msg = "ERROR setting %s in storm active state: %s " % (
+ _dict_key, e)
+ ecomp_logger(tds.LOG_TYPE_ERROR, tds.SEV_ERROR,
+ tds.CODE_GENERAL, msg)
+ return True
+ else:
+ return False
+
+# # # # # # # # # # # # #
+# fx: sw_reset_counter_dict
+# - reset counter dictionary on <interval> boundaries
+# # # # # # # # # # # # #
+
+
+def sw_reset_counter_dict():
+ """
+ Create new storm_active_dict based on quantities received during
+ last sample interval
+ :Parameters:
+ :Exceptions:
+ none
+ :Keywords:
+ stormwatch count threshold
+ :Variables:
+ """
+
+ # <stats>
+ # publish stats to MR...
+ try:
+ msg = "publish counts-by-oid from stats.oid_counter_dict"
+ ecomp_logger(tds.LOG_TYPE_DEBUG, tds.SEV_INFO,
+ tds.CODE_GENERAL, msg)
+ msg = "publish count-by-agent from stats.agent_counter_dict"
+ ecomp_logger(tds.LOG_TYPE_DEBUG, tds.SEV_INFO,
+ tds.CODE_GENERAL, msg)
+ except Exception as e:
+ msg = "unable to publish counts by oid and agent to MR: " % (e)
+ ecomp_logger(tds.LOG_TYPE_ERROR, tds.SEV_WARN, tds.CODE_GENERAL, msg)
+
+ # ...and now reset stats counters to start next interval
+ try:
+ stats.oid_counter_dict.clear()
+ stats.agent_counter_dict.clear()
+ stats.total_notifications = 0
+ except Exception as e:
+ msg = "unable to reset counts by oid and agent dictionaries - stats will be INNACURATE: " % (
+ e)
+ ecomp_logger(tds.LOG_TYPE_ERROR, tds.SEV_WARN, tds.CODE_GENERAL, msg)
+ # </stats>
+
+ # <storm watch active>
+
+ # for k in sws.sw_storm_active_dict:
+ # File "./snmptrapd.py", line 642, in notif_receiver_cb
+ # if stormwatch.sw_storm_active(tds.trap_dict["agent address"], tds.trap_dict["notify OID"]):
+ # File "/opt/app/snmptrap/bin/mod/trapd_stormwatch.py", line 299, in sw_storm_active
+ # sw_reset_counter_dict()
+ # File "/opt/app/snmptrap/bin/mod/trapd_stormwatch.py", line 381, in sw_reset_counter_dict
+ # for k in sws.sw_storm_active_dict:
+ # RuntimeError: dictionary changed size during iteration
+ # FIXME: changed to "for k in list(sw_storm_active_dict)" as explained here:
+ # see https://stackoverflow.com/questions/20418851/delete-an-entry-from-a-dictionary-python
+
+ for k in list(sws.sw_storm_active_dict):
+ _loc_agent = k.split()[0]
+ _loc_oid = k.split()[1]
+
+ _high_water_val = sws.sw_config_high_water_in_interval_dict[_loc_oid]
+
+ if sws.sw_storm_counter_dict[k] >= _high_water_val:
+ msg = "%s remaining in storm state, received %d events (GE to upper threshold: %d)" % (
+ k, sws.sw_storm_counter_dict[k], _high_water_val)
+ ecomp_logger(tds.LOG_TYPE_DEBUG, tds.SEV_INFO,
+ tds.CODE_GENERAL, msg)
+ sws.sw_storm_counter_dict[k] = 0
+ else:
+ _low_water_val = sws.sw_config_low_water_in_interval_dict[_loc_oid]
+ if sws.sw_storm_counter_dict[k] < _low_water_val:
+ try:
+ msg = "STORM OVER: received %d events (%s) from %s (less than low water threshold: %d)" % (
+ sws.sw_storm_counter_dict[k], _loc_oid, _loc_agent, _low_water_val)
+ ecomp_logger(tds.LOG_TYPE_AUDIT, tds.SEV_WARN,
+ tds.CODE_GENERAL, msg)
+ del sws.sw_storm_active_dict[k]
+ sws.sw_storm_counter_dict[k] = 0
+ except Exception as e:
+ msg = "unable to remove %s from storm active dictionary - TRAPS MAY BE DISCARDED UNINTENTIONALLY! Reason: %s " % (
+ k, e)
+ ecomp_logger(tds.LOG_TYPE_ERROR, tds.SEV_ERROR,
+ tds.CODE_GENERAL, msg)
+ else:
+ msg = "%s remaining in storm state, received %d events (GE to lower threshold: %d)" % (
+ k, sws.sw_storm_counter_dict[k], _low_water_val)
+ ecomp_logger(tds.LOG_TYPE_ERROR, tds.SEV_INFO,
+ tds.CODE_GENERAL, msg)
+ sws.sw_storm_counter_dict[k] = 0
+
+ sws.sw_last_stormwatch_dict_analysis = int(time.time())
+
+ return True
+
+# # # # # # # # # # # # #
+# fx: sw_increment_counter
+# - increment OID and agent trap counters
+# based on arriving trap attributes
+# # # # # # # # # # # # #
+
+
+def sw_increment_counter(_dict_key):
+ """
+ Add to appropriate counter based on arriving trap
+ agent and OID
+ :Parameters:
+ _dict_key
+ agent address from trap PDU and notify OID
+ trap PDU, separated by a space
+ :Exceptions:
+ none
+ :Keywords:
+ stormwatch count threshold
+ :Variables:
+ """
+
+ try:
+ sws.sw_storm_counter_dict[_dict_key] += 1
+ msg = "stormwatch counter for %s now: %d" % (
+ _dict_key, sws.sw_storm_counter_dict[_dict_key])
+ ecomp_logger(tds.LOG_TYPE_DEBUG, tds.SEV_INFO,
+ tds.CODE_GENERAL, msg)
+ return True
+ except Exception as E:
+ msg = "first trap for %s - init stormwatch counter to 1" % (_dict_key)
+ ecomp_logger(tds.LOG_TYPE_DEBUG, tds.SEV_INFO,
+ tds.CODE_GENERAL, msg)
+ sws.sw_storm_counter_dict[_dict_key] = 1
+ return True
diff --git a/snmptrap/mod/trapd_stormwatch_settings.py b/snmptrap/mod/trapd_stormwatch_settings.py
new file mode 100644
index 0000000..f2586f9
--- /dev/null
+++ b/snmptrap/mod/trapd_stormwatch_settings.py
@@ -0,0 +1,61 @@
+# ============LICENSE_START=======================================================
+# Copyright (c) 2020 AT&T Intellectual Property. All rights reserved.
+# ================================================================================
+# 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.
+# ============LICENSE_END=========================================================
+"""
+"""
+
+__docformat__ = 'restructuredtext'
+
+
+def init():
+
+ # <Storm Watch>
+ #
+ # sw_storm_counter_dict
+ # key [<ip address>.<notify oid>] -> count
+ #
+ # sw_storm_active_dict
+ # key [<ip address>.<notify oid>] -> True (or False, but no key present
+ # means False)
+ #
+ # sw_config_oid_dict
+ # key [<notify oid>] -> "true" (key presence means it participates)
+ #
+ # sw_config_low_water_in_interval_dict
+ # key [<notify oid>] -> <int> value that stormActive turns False
+ #
+ # sw_config_high_water_in_interval_dict
+ # key [<notify oid>] -> <int> value that stormActive turns True
+ #
+ global sw_storm_counter_dict
+ sw_storm_counter_dict = {}
+
+ global sw_storm_active_dict
+ sw_storm_active_dict = {}
+
+ global sw_config_oid_dict
+ sw_config_oid_dict = {}
+ global sw_config_low_water_in_interval_dict
+ sw_config_low_water_in_interval_dict = {}
+ global sw_config_high_water_in_interval_dict
+ sw_config_high_water_in_interval_dict = {}
+ global sw_config_category
+ sw_config_category = {}
+
+ global sw_interval_in_seconds
+ sw_interval_in_seconds = 60
+ global sw_last_stormwatch_dict_analysis
+ sw_last_stormwatch_dict_analysis = 0
+ # </Storm Watch>
diff --git a/snmptrap/mod/trapd_vb_types.py b/snmptrap/mod/trapd_vb_types.py
index 2d01a30..98d5d2c 100644
--- a/snmptrap/mod/trapd_vb_types.py
+++ b/snmptrap/mod/trapd_vb_types.py
@@ -1,7 +1,5 @@
# ============LICENSE_START=======================================================
-# org.onap.dcae
-# ================================================================================
-# Copyright (c) 2018 AT&T Intellectual Property. All rights reserved.
+# Copyright (c) 2018-2020 AT&T Intellectual Property. All rights reserved.
# ================================================================================
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
@@ -15,9 +13,6 @@
# See the License for the specific language governing permissions and
# limitations under the License.
# ============LICENSE_END=========================================================
-#
-# ECOMP is a trademark and service mark of AT&T Intellectual Property.
-#
"""
module for converting varbind types from Net-SNMP to PYSNMP
@@ -47,22 +42,24 @@ prog_name = os.path.basename(__file__)
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
_pysnmp_to_netsnmp_vb_type = {
- 'Integer' : 'integer',
- 'Gauge32' : 'unsigned',
- 'Counter32' : 'counter32',
- 'OctetString' : 'octet',
- 'py_type_5' : 'hex',
- 'py_type_6' : 'decimal',
- 'Null' : 'null',
- 'ObjectIdentifier' : 'oid',
- 'TimeTicks' : 'timeticks',
- 'IpAddress' : 'ipaddress',
- 'Bits' : 'bits'
- }
+ 'Integer32': 'integer',
+ 'Integer': 'integer',
+ 'Gauge32': 'unsigned',
+ 'Counter32': 'counter32',
+ 'OctetString': 'octet',
+ 'py_type_5': 'hex',
+ 'py_type_6': 'decimal',
+ 'Null': 'null',
+ 'ObjectIdentifier': 'oid',
+ 'TimeTicks': 'timeticks',
+ 'IpAddress': 'ipaddress',
+ 'Bits': 'bits'
+}
default_vb_type = "octet"
-def pysnmp_to_netsnmp_varbind_convert (_pysnmp_vb_type):
+
+def pysnmp_to_netsnmp_varbind_convert(_pysnmp_vb_type):
"""
Convert pysnmp varbind types to Net-SNMP nomenclature
to maintain backward compatibilty with existing solutions
@@ -76,14 +73,11 @@ def pysnmp_to_netsnmp_varbind_convert (_pysnmp_vb_type):
# lookup _pysnmp_vb_type in conversion dictionary
try:
- msg = ("checking for netsnmp equiv of varbind type: %s" \
- % _pysnmp_vb_type)
- ecomp_logger(tds.LOG_TYPE_DEBUG, tds.SEV_INFO, tds.CODE_GENERAL, msg)
_netsnmp_vb_type = _pysnmp_to_netsnmp_vb_type[_pysnmp_vb_type]
return _netsnmp_vb_type
except Exception as e:
# if not found, return original pysnmp type
- msg = ("%s not configured as pysnmp varbind type" \
- % _pysnmp_vb_type)
+ msg = ("%s not configured as pysnmp varbind type - returning %s"
+ % (_pysnmp_vb_type,default_vb_type))
ecomp_logger(tds.LOG_TYPE_DEBUG, tds.SEV_INFO, tds.CODE_GENERAL, msg)
return default_vb_type
diff --git a/snmptrap/scheduler.sh b/snmptrap/scheduler.sh
new file mode 100755
index 0000000..bb2d083
--- /dev/null
+++ b/snmptrap/scheduler.sh
@@ -0,0 +1,186 @@
+#!/usr/bin/env bash
+# ============LICENSE_START=======================================================
+# Copyright (c) 2017-2020 AT&T Intellectual Property. All rights reserved.
+# ================================================================================
+# 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.
+# ============LICENSE_END=========================================================
+#
+#
+
+script_name=`basename "$0"`
+
+# sleep_time=.1 # in seconds
+sleep_time=1 # in seconds
+
+log_fd=/var/tmp/${script_name}.log
+log_lines=0
+max_log_lines=4000
+
+num_days_to_keep_logs=7
+
+# find the best tool for the job
+if [ `command -v pypy3` ]
+then
+ PY_BINARY=pypy3
+else
+ if [ `command -v python3` ]
+ then
+ PY_BINARY=python3
+ else
+ if [ `command -v python` ]
+ then
+ PY_BINARY=python
+ else
+ echo "ERROR: no pypy3 or python available in container - FATAL ERROR, exiting"
+ exit 1
+ fi
+ fi
+fi
+
+# logging utility
+logFx()
+{
+_fx=$1
+# skipping verbosity setting
+_verbosity=$2
+_log_message=$3
+
+ echo "`date` `date +%s` ${_fx} ${_error_code} ${_log_message}" >> ${log_fd}
+
+ # log_lines=`wc -l ${log_fd} | awk {'print $1'} | bc`
+ # log_lines=$((${log_lines}+1))
+
+ # if [ ${log_lines} -ge ${max_log_lines} ]
+ # then
+ # if [ -f ${log_fd} ]
+ # then
+ # mv -f ${log_fd} ${log_fd}.old
+ # fi
+ # fi
+}
+
+run_10sec_jobs()
+{
+ # send heartbeat
+ ${PY_BINARY} /opt/app/snmptrap/bin/send_hb_trap localhost 6162 > /var/tmp/send_hb_trap.out 2>&1 &
+ rc=$?
+ logFx ${FUNCNAME[0]} 0 "send_hb_trap returned ${rc}"
+
+ # other 10 second jobs below here
+
+ return ${rc}
+}
+
+run_minute_jobs()
+{
+rc=0
+ # add other minute jobs below here
+
+ return ${rc}
+}
+
+run_hourly_jobs()
+{
+ # no hourly jobs scheduled at this time
+ rc=0
+
+ # add other hourly jobs below here
+
+ return ${rc}
+}
+
+run_daily_jobs()
+{
+ rc=0
+
+ # remove old logs
+ for f in `find /opt/app/snmptrap/logs -type f -mtime +${num_days_to_keep_logs}`
+ do
+ logFx ${FUNCNAME[0]} 0 "removing $f"
+ rm $f
+ done
+
+ # move scheduler log_fd to daily backup
+ mv -f ${log_fd} ${log_fd}.`date +%a`
+
+ # add other daily jobs below here
+
+ return ${rc}
+}
+
+
+# # # # # # # # # # # # # #
+# main HCCCKK area
+# # # # # # # # # # # # # #
+
+begin_minute=`date +%M | bc`
+
+# wait for minute to roll to new one
+logFx ${FUNCNAME[0]} 0 "waiting for new minute..."
+while [ ${begin_minute} -eq `date +%M | bc` ]
+do
+ sleep .1
+done
+
+SECONDS=0
+logFx ${FUNCNAME[0]} 0 "scheduler synced to new minute"
+last_minute=`date +%M`
+last_hour=`date +%H`
+last_day=`date +%j`
+
+logFx ${FUNCNAME[0]} 0 "entering endless loop"
+while(true)
+do
+ if [ $SECONDS -ge 10 ]
+ then
+ # run every 10 seconds jobs
+ logFx ${FUNCNAME[0]} 0 "$SECONDS seconds have elapsed - calling run_10sec_jobs"
+ run_10sec_jobs
+ # reset SECONDS
+ SECONDS=0
+
+ # check for minute change
+ current_minute=`date +%M | bc`
+ if [ ${current_minute} -ne ${last_minute} ]
+ then
+ # run every minute jobs
+ logFx ${FUNCNAME[0]} 0 "minute change from ${last_minute} to ${current_minute} - calling run_minute_jobs"
+ run_minute_jobs
+ # reset last_minute
+ last_minute=${current_minute}
+
+ # check for hour change
+ current_hour=`date +%H | bc`
+ if [ ${current_hour} -ne ${last_hour} ]
+ then
+ # run every hour jobs
+ logFx ${FUNCNAME[0]} 0 "hour change from ${last_hour} to ${current_hour} - calling run_hourly_jobs"
+ run_hourly_jobs
+ # reset last_minute
+ last_hour=${current_hour}
+
+ # check for day change
+ current_day=`date +%j | bc`
+ if [ ${current_day} -ne ${last_day} ]
+ then
+ # run every day jobs
+ logFx ${FUNCNAME[0]} 0 "day change from ${last_day} to ${current_day} - calling run_daily_jobs"
+ run_daily_jobs
+ # reset last_day
+ last_day=${current_day}
+ fi
+ fi
+ fi
+ fi
+ sleep ${sleep_time}
+done
diff --git a/snmptrap/send_hb_trap b/snmptrap/send_hb_trap
new file mode 100755
index 0000000..efec02c
--- /dev/null
+++ b/snmptrap/send_hb_trap
@@ -0,0 +1,65 @@
+# ============LICENSE_START=======================================================
+# Copyright (c) 2020 AT&T Intellectual Property. All rights reserved.
+# ================================================================================
+# 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.
+# ============LICENSE_END=========================================================
+import time
+from pysnmp.hlapi import *
+from pysnmp.error import PySnmpError
+import argparse
+import os
+
+parser = argparse.ArgumentParser()
+parser.add_argument("dest", help="trap receiver hostname or ip address")
+parser.add_argument("port", help="trap receiver port number", type=int)
+args = parser.parse_args()
+
+env_service_name = os.getenv('SERVICE_NAME')
+env_service_tags = os.getenv('SERVICE_TAGS')
+
+if env_service_name is None or env_service_tags is None:
+ now = time.strftime("%Y-%m-%d %H:%M:%S %Z", time.localtime())
+ print('%s Cannot get SERVICE_NAME and/or SERVICE_TAGS env vars'
+ % now)
+ if env_service_name is None:
+ env_service_name = 'SERVICE_NAME N/A'
+ if env_service_tags is None:
+ env_service_tags = 'SERVICE_TAGS N/A'
+
+try:
+ errorIndication, errorStatus, errorIndex, varBinds = next(
+ sendNotification(
+ SnmpEngine(),
+ CommunityData('public', mpModel=1),
+ UdpTransportTarget((args.dest, args.port)),
+ ContextData(),
+ 'trap',
+ NotificationType(
+ ObjectIdentity('.1.3.6.1.4.1.74.2.46.12.1.1')
+ ).addVarBinds(
+ ('.1.3.6.1.4.1.74.2.46.12.1.1.1',
+ OctetString('onap trapd heartbeat')),
+ ('.1.3.6.1.4.1.74.2.46.12.1.1.2',
+ OctetString(time.ctime())),
+ ('.1.3.6.1.4.1.74.2.46.12.1.1.3',
+ OctetString(env_service_name)),
+ ('.1.3.6.1.4.1.74.2.46.12.1.1.4',
+ OctetString(env_service_tags))
+ )
+ )
+ )
+ if errorIndication:
+ print(errorIndication)
+
+except PySnmpError as e:
+ print("Exception from sendNotification: %s" % e)
diff --git a/snmptrap/snmptrapd.py b/snmptrap/snmptrapd.py
index 8a824ef..5f46f34 100644
--- a/snmptrap/snmptrapd.py
+++ b/snmptrap/snmptrapd.py
@@ -1,7 +1,5 @@
-# ============LICENSE_START=======================================================)
-# org.onap.dcae
-# ================================================================================
-# Copyright (c) 2017-2018 AT&T Intellectual Property. All rights reserved.
+# ============LICENSE_START=======================================================
+# Copyright (c) 2017-2020 AT&T Intellectual Property. All rights reserved.
# ================================================================================
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
@@ -15,11 +13,8 @@
# See the License for the specific language governing permissions and
# limitations under the License.
# ============LICENSE_END=========================================================
-#
-# ECOMP is a trademark and service mark of AT&T Intellectual Property.
-#
"""
-dcae_snmptrapd is responsible for SNMP trap receipt and publishing activities.
+snmptrapd is responsible for SNMP trap receipt and publishing activities.
It's behavior is controlled by CBS (config binding service) using a
JSON construct obtained via a "get_config" call or (for testing/standalone
purposes) a file specified using the env variable "CBS_SIM_JSON".
@@ -28,7 +23,7 @@ As traps arrive they are decomposed and transformed into a JSON message which
is published to a dmaap instance that has been defined by controller.
:Parameters:
- usage: snmptrapd.py
+ usage: snmptrapd.py [-v]
:Keywords:
onap dcae snmp trap publish dmaap
"""
@@ -67,15 +62,23 @@ from pysnmp.entity.rfc3413 import ntfrcv
from pysnmp.proto.api import v2c
from pysnmp import debug
-# dcae_snmptrap
+# snmptrap
import trapd_settings as tds
+
from trapd_runtime_pid import save_pid, rm_pid
from trapd_get_cbs_config import get_cbs_config
from trapd_exit import cleanup_and_exit
-from trapd_http_session import init_session_obj, close_session_obj, reset_session_obj
+from trapd_http_session import init_session_obj, close_session_obj,\
+ reset_session_obj
from trapd_snmpv3 import load_snmpv3_credentials
from trapd_vb_types import pysnmp_to_netsnmp_varbind_convert
-from trapd_io import roll_all_logs, open_eelf_logs, roll_file, open_file, close_file, ecomp_logger, stdout_logger
+from trapd_io import roll_all_logs, open_eelf_logs, roll_file, open_file,\
+ close_file, ecomp_logger, stdout_logger
+
+import trapd_stormwatch_settings as sws
+import trapd_stormwatch as stormwatch
+
+import trapd_stats_settings as stats
prog_name = os.path.basename(__file__)
verbose = False
@@ -97,7 +100,7 @@ def usage_err():
"""
print('Incorrect usage invoked. Correct usage:')
- print(' %s' % prog_name)
+ print(' %s [-v]' % prog_name)
cleanup_and_exit(1, "undefined")
@@ -126,38 +129,83 @@ def load_all_configs(_signum, _frame):
if int(_signum) != 0:
msg = ("received signal %s at frame %s; re-reading configs"
% (_signum, _frame))
- ecomp_logger(tds.LOG_TYPE_DEBUG, tds.SEV_DETAILED,
+ ecomp_logger(tds.LOG_TYPE_DEBUG, tds.SEV_INFO,
tds.CODE_GENERAL, msg)
- # re-request config (from broker, or local json file
- # if broker not present)
+ # re-request config from broker:
if not get_cbs_config():
msg = "Error (re)loading CBS config - FATAL ERROR, exiting"
stdout_logger(msg)
cleanup_and_exit(1, tds.pid_file_name)
else:
- current_runtime_config_file_name = tds.c_config['files']['runtime_base_dir'] + \
- "/tmp/current_config.json"
+ current_runtime_config_file_name = (
+ tds.c_config['files']['runtime_base_dir'] +
+ "/tmp/current_config.json")
if int(_signum) != 0:
- msg = "updated config logged to : %s" % current_runtime_config_file_name
+ msg = "updated config logged to : %s" % \
+ current_runtime_config_file_name
else:
- msg = "current config logged to : %s" % current_runtime_config_file_name
- ecomp_logger(tds.LOG_TYPE_DEBUG, tds.SEV_INFO, tds.CODE_GENERAL, msg)
+ msg = "current config logged to : %s" % \
+ current_runtime_config_file_name
+ ecomp_logger(tds.LOG_TYPE_DEBUG, tds.SEV_INFO, tds.CODE_GENERAL,
+ msg)
- with open(current_runtime_config_file_name, 'w') as outfile:
- json.dump(tds.c_config, outfile)
+ with open(current_runtime_config_file_name, 'w') as outfile:
+ json.dump(tds.c_config, outfile)
# reset http session based on latest config
tds.http_requ_session = reset_session_obj(tds.http_requ_session)
- # FMDL: add with stormWatch
# reload sw participating entries, reset counter dictionary
- # sw.interval_in_seconds, sw.participant_oid_dict = load_sw_participant_dict(tds.c_config['trap_config'])
- # sw.counter_dict = init_counter_dict()
+ traps_configured = stormwatch.sw_load_trap_config(tds.c_config)
+ msg = "encountered %d trap configurations in CBS/json config" % \
+ traps_configured
+ ecomp_logger(tds.LOG_TYPE_DEBUG, tds.SEV_INFO, tds.CODE_GENERAL,
+ msg)
- # if here, config re-read successfully
+ tds.last_minute = datetime.datetime.now().minute
+ tds.last_hour = datetime.datetime.now().hour
+ tds.last_day = datetime.datetime.now().day
+
+ # if here, configs re-read successfully
return True
+
+# # # # # # # # # # # # #
+# fx: resolve_ip
+# # # # # # # # # # # # #
+def resolve_ip(_loc_ip_addr_str):
+
+ try:
+ if int(tds.dns_cache_ip_expires[_loc_ip_addr_str] < int(time.time())):
+ raise Exception('cache expired for %s at %d - updating value' %
+ (_loc_ip_addr_str,
+ (tds.dns_cache_ip_expires[_loc_ip_addr_str])))
+ else:
+ agent_fqdn = tds.dns_cache_ip_to_name[_loc_ip_addr_str]
+
+ except Exception as e:
+ msg = "dns cache expired or missing for %s - refreshing" % \
+ _loc_ip_addr_str
+ ecomp_logger(tds.LOG_TYPE_DEBUG, tds.SEV_INFO,
+ tds.CODE_GENERAL, msg)
+ try:
+ agent_fqdn, alias, addresslist = socket.gethostbyaddr(
+ _loc_ip_addr_str)
+ except Exception as e:
+ agent_fqdn = _loc_ip_addr_str
+
+ tds.dns_cache_ip_to_name[_loc_ip_addr_str] = agent_fqdn
+ tds.dns_cache_ip_expires[_loc_ip_addr_str] = (
+ time.time() + int(tds.c_config['cache']['dns_cache_ttl_seconds']))
+ msg = "cache for %s (%s) updated - set to expire at %d" % \
+ (agent_fqdn, _loc_ip_addr_str,
+ tds.dns_cache_ip_expires[_loc_ip_addr_str])
+ ecomp_logger(tds.LOG_TYPE_DEBUG, tds.SEV_INFO,
+ tds.CODE_GENERAL, msg)
+
+ return agent_fqdn
+
# # # # # # # # # # # # #
# fx: log_all_arriving_traps
# # # # # # # # # # # # #
@@ -185,29 +233,29 @@ def log_all_arriving_traps():
# always log arriving trap
try:
- # going for:
- # 1520971776 Tue Mar 13 16:09:36 2018; 1520971776 2018-03-13 16:09:36 DCAE-COLLECTOR-UCSNMP 15209717760049 .1.3.6.1.4.1.2636.4.1.6 gfpmt5pcs10.oss.att.com 135.91.10.139 12.123.1.240 12.123.1.240 2 varbinds: [0] .1.3.6.1.2.1.1.3.0 {10} 1212058366 140 days, 6:49:43.66 [1] .1.3.6.1.6.3.1.1.4.1.0 {6} .1.3.6.1.4.1.2636.4.1.6 [2] .1.3.6.1.4.1.2636.3.1.15.1.1.2.4.0.0 {2} 2 [3] .1.3.6.1.4.1.2636.3.1.15.1.2.2.4.0.0 {2} 4 [4] .1.3.6.1.4.1.2636.3.1.15.1.3.2.4.0.0 {2} 0 [5] .1.3.6.1.4.1.2636.3.1.15.1.4.2.4.0.0 {2} 0 [6] .1.3.6.1.4.1.2636.3.1.15.1.5.2.4.0.0 {4} PEM 3 [7] .1.3.6.1.4.1.2636.3.1.15.1.6.2.4.0.0 {2} 7 [8] .1.3.6.1.4.1.2636.3.1.15.1.7.2.4.0.0 {2} 4 [9] .1.3.6.1.6.3.18.1.3.0 {7} 12.123.1.240
-
- tds.arriving_traps_fd.write('%s %s; %s %s %s %s %s %s %s %s %s %s\n' %
- (tds.trap_dict["time received"],
- time.strftime(
- "%a %b %d %H:%M:%S %Y", time.localtime(time.time())),
- time.strftime("%a %b %d %H:%M:%S %Y", time.localtime(
- tds.trap_dict["time received"])),
- tds.trap_dict["trap category"],
- tds.trap_dict["epoch_serno"],
- tds.trap_dict["notify OID"],
- tds.trap_dict["agent name"],
- tds.trap_dict["agent address"],
- tds.trap_dict["cambria.partition"],
- tds.trap_dict["protocol version"],
- tds.trap_dict["uuid"],
- tds.all_vb_json_str))
+ time_now=int(round(time.time(),0))
+ arrived_epoch_time_int=int(tds.trap_dict["time received"])
+ tds.arriving_traps_fd.write('%d %s; %s %s %s %s %s %s %s %s %s %s %s %s %s\n' %
+ (time_now,
+ datetime.datetime.fromtimestamp(time_now).strftime("%a %b %d %H:%M:%S %Y"),
+ tds.trap_dict["time received"],
+ datetime.datetime.fromtimestamp(arrived_epoch_time_int).strftime("%a %b %d %H:%M:%S %Y"),
+ tds.trap_dict["trap category"],
+ tds.trap_dict["epoch_serno"],
+ tds.trap_dict["notify OID"],
+ tds.trap_dict["agent name"],
+ tds.trap_dict["agent address"],
+ tds.trap_dict["pdu agent name"],
+ tds.trap_dict["pdu agent address"],
+ tds.trap_dict["cambria.partition"],
+ tds.trap_dict["protocol version"],
+ tds.trap_dict["uuid"],
+ tds.all_vb_str))
except Exception as e:
msg = "Error writing to %s : %s - arriving trap %s NOT LOGGED" % (
tds.arriving_traps_filename, str(e), tds.trap_dict["uuid"])
- ecomp_logger(tds.LOG_TYPE_ERROR, tds.SEV_CRIT, tds.CODE_GENERAL, msg)
+ ecomp_logger(tds.LOG_TYPE_ERROR, tds.SEV_ERROR, tds.CODE_GENERAL, msg)
# # # # # # # # # # # # #
@@ -227,12 +275,12 @@ def log_published_messages(_post_data_enclosed):
tds.json_traps_fd.write('%s\n' % _post_data_enclosed)
msg = "successfully logged json for %s to %s" % (
tds.trap_dict["uuid"], tds.json_traps_filename)
- ecomp_logger(tds.LOG_TYPE_DEBUG, tds.SEV_DETAILED,
+ ecomp_logger(tds.LOG_TYPE_DEBUG, tds.SEV_INFO,
tds.CODE_GENERAL, msg)
except Exception as e:
msg = "Error writing to %s : %s - trap %s NOT LOGGED" % (
tds.json_traps_filename, str(e), tds.trap_dict["uuid"])
- ecomp_logger(tds.LOG_TYPE_ERROR, tds.SEV_CRIT, tds.CODE_GENERAL, msg)
+ ecomp_logger(tds.LOG_TYPE_ERROR, tds.SEV_ERROR, tds.CODE_GENERAL, msg)
# # # # # # # # # # # # #
@@ -255,19 +303,19 @@ def post_dmaap():
if tds.http_requ_session is None:
msg = "tds.http_requ_session is None - getting new (%s)" % tds.http_requ_session
- ecomp_logger(tds.LOG_TYPE_DEBUG, tds.SEV_DETAILED,
+ ecomp_logger(tds.LOG_TYPE_DEBUG, tds.SEV_INFO,
tds.CODE_GENERAL, msg)
tds.http_requ_session = init_session_obj()
# if only 1 trap, ship as-is
if tds.traps_since_last_publish == 1:
- post_data_enclosed = tds.all_traps_str
+ post_data_enclosed = tds.all_traps_json_str
else:
# otherwise, add brackets around package
- post_data_enclosed = '[' + tds.all_traps_str + ']'
+ post_data_enclosed = '[' + tds.all_traps_json_str + ']'
msg = "post_data_enclosed: %s" % (post_data_enclosed)
- ecomp_logger(tds.LOG_TYPE_DEBUG, tds.SEV_DETAILED, tds.CODE_GENERAL, msg)
+ ecomp_logger(tds.LOG_TYPE_DEBUG, tds.SEV_INFO, tds.CODE_GENERAL, msg)
k = 0
dmaap_pub_success = False
@@ -277,7 +325,7 @@ def post_dmaap():
if tds.c_config['streams_publishes']['sec_fault_unsecure']['aaf_username'] == "" or tds.c_config['streams_publishes']['sec_fault_unsecure']['aaf_username'] is None:
msg = "%d trap(s) : %s - attempt %d (unsecure)" % (
tds.traps_since_last_publish, tds.trap_uuids_in_buffer, k)
- ecomp_logger(tds.LOG_TYPE_DEBUG, tds.SEV_DETAILED,
+ ecomp_logger(tds.LOG_TYPE_DEBUG, tds.SEV_INFO,
tds.CODE_GENERAL, msg)
http_resp = tds.http_requ_session.post(tds.c_config['streams_publishes']['sec_fault_unsecure']['dmaap_info']['topic_url'], post_data_enclosed,
headers=http_headers,
@@ -285,7 +333,7 @@ def post_dmaap():
else:
msg = "%d trap(s) : %s - attempt %d (secure)" % (
tds.traps_since_last_publish, tds.trap_uuids_in_buffer, k)
- ecomp_logger(tds.LOG_TYPE_DEBUG, tds.SEV_DETAILED,
+ ecomp_logger(tds.LOG_TYPE_DEBUG, tds.SEV_INFO,
tds.CODE_GENERAL, msg)
http_resp = tds.http_requ_session.post(tds.c_config['streams_publishes']['sec_fault_unsecure']['dmaap_info']['topic_url'], post_data_enclosed,
auth=(tds.c_config['streams_publishes']['sec_fault_unsecure']['aaf_username'],
@@ -309,7 +357,7 @@ def post_dmaap():
tds.CODE_GENERAL, msg)
except OSError as e:
- msg = "OS exception while attempting to post %s attempt %s: (%s) %s %s" % (
+ msg = "OS exception while attempting to post %s attempt %d: (%s) %s %s" % (
tds.trap_uuids_in_buffer, k, e.errno, e.strerror, str(e))
ecomp_logger(tds.LOG_TYPE_ERROR, tds.SEV_WARN,
tds.CODE_GENERAL, msg)
@@ -322,10 +370,10 @@ def post_dmaap():
k += 1
- if k < tds.c_config['publisher']['http_retries']:
+ if k < int(tds.c_config['publisher']['http_retries']):
msg = "sleeping %.4f seconds and retrying" % (
tds.seconds_between_retries)
- ecomp_logger(tds.LOG_TYPE_DEBUG, tds.SEV_DETAILED,
+ ecomp_logger(tds.LOG_TYPE_ERROR, tds.SEV_WARN,
tds.CODE_GENERAL, msg)
time.sleep(tds.seconds_between_retries)
else:
@@ -334,12 +382,12 @@ def post_dmaap():
if not dmaap_pub_success:
msg = "ALL publish attempts failed for traps %s to URL %s "\
% (tds.trap_uuids_in_buffer, tds.c_config['streams_publishes']['sec_fault_unsecure']['dmaap_info']['topic_url'])
- ecomp_logger(tds.LOG_TYPE_ERROR, tds.SEV_CRIT, tds.CODE_GENERAL, msg)
+ ecomp_logger(tds.LOG_TYPE_ERROR, tds.SEV_ERROR, tds.CODE_GENERAL, msg)
# FMDL: This currently tries, then logs error and trashes buffer if all dmaap attempts fail. Better way?
tds.traps_since_last_publish = 0
tds.trap_uuids_in_buffer = ""
- tds.all_traps_str = ""
+ tds.all_traps_json_str = ""
tds.first_trap = True
# # # # # # # # # # # # # # # # # # #
@@ -410,6 +458,7 @@ def snmp_engine_observer_cb(snmp_engine, execpoint, variables, cbCtx):
tds.traps_in_epoch = 0
tds.last_epoch_second = epoch_second
traps_in_epoch_04d = format(tds.traps_in_epoch, '04d')
+ tds.trap_dict['epoch_arrived'] = epoch_second
tds.trap_dict['epoch_serno'] = int(
(str(epoch_second) + str(traps_in_epoch_04d)))
@@ -418,36 +467,22 @@ def snmp_engine_observer_cb(snmp_engine, execpoint, variables, cbCtx):
# ip and hostname
ip_addr_str = str(variables['transportAddress'][0])
+ # set agent address and name to source of packet, OVERWRITE if
+ # .1.3.6.1.6.3.18.1.3.0 varbind encountered later in trap processing
tds.trap_dict["agent address"] = ip_addr_str
+ tds.trap_dict["agent name"] = resolve_ip(ip_addr_str)
+ # set overridden/logical address and name to source of packet so we know
+ # original value if .1.3.6.1.6.3.18.1.3.0 shows up
+ # NOTE: This does NOT change ever, label may change to
+ # "overridden agent..." in the future for truth in nameing
+ tds.trap_dict["pdu agent address"] = tds.trap_dict["agent address"]
+ tds.trap_dict["pdu agent name"] = tds.trap_dict["agent name"]
+
+ # log arrival now that we have agent addr
+ msg = 'trap from %s %s, assigned uuid: %s' % \
+ (ip_addr_str, tds.trap_dict["agent name"], tds.trap_dict["uuid"])
+ ecomp_logger(tds.LOG_TYPE_DEBUG, tds.SEV_INFO, tds.CODE_GENERAL, msg)
- msg = 'snmp trap arrived from %s, assigned uuid: %s' % \
- (ip_addr_str, tds.trap_dict["uuid"])
- ecomp_logger(tds.LOG_TYPE_DEBUG, tds.SEV_DETAILED, tds.CODE_GENERAL, msg)
-
- try:
- if int(tds.dns_cache_ip_expires[ip_addr_str] < int(time.time())):
- raise Exception('cache expired for %s at %d - updating value' %
- (ip_addr_str, (tds.dns_cache_ip_expires[ip_addr_str])))
- else:
- tds.trap_dict["agent name"] = tds.dns_cache_ip_to_name[ip_addr_str]
- except Exception as e:
- msg = "dns cache expired or missing for %s - refreshing" % ip_addr_str
- ecomp_logger(tds.LOG_TYPE_DEBUG, tds.SEV_DETAILED,
- tds.CODE_GENERAL, msg)
- try:
- agent_fqdn, alias, addresslist = socket.gethostbyaddr(ip_addr_str)
- except Exception as e:
- agent_fqdn = ip_addr_str
-
- tds.trap_dict["agent name"] = agent_fqdn
-
- tds.dns_cache_ip_to_name[ip_addr_str] = agent_fqdn
- tds.dns_cache_ip_expires[ip_addr_str] = (
- time.time() + tds.c_config['cache']['dns_cache_ttl_seconds'])
- msg = "cache for %s (%s) updated - set to expire at %d" % \
- (agent_fqdn, ip_addr_str, tds.dns_cache_ip_expires[ip_addr_str])
- ecomp_logger(tds.LOG_TYPE_DEBUG, tds.SEV_DETAILED,
- tds.CODE_GENERAL, msg)
tds.trap_dict["cambria.partition"] = str(tds.trap_dict["agent name"])
# do not include cleartext community in pub
@@ -491,6 +526,42 @@ def snmp_engine_observer_cb(snmp_engine, execpoint, variables, cbCtx):
# # # # # # # # # # # # # # # # # # #
# fx: request_observer for community string rewrite
# # # # # # # # # # # # # # # # # # #
+
+def add_varbind_to_log_string(vb_idx, vb_oid, vb_type, vb_val):
+ """
+ Called for each varbind, adds individual attributes of varbind instance to
+ all_vb_str for logging.
+ :Parameters:
+ vb_idx
+ index to specific varbind being processed
+ vb_oid
+ the varbind oid
+ vb_type
+ the varbind type
+ vb_val
+ the value of the varbind
+ :Exceptions:
+ none
+ :Keywords:
+ varbind extract log
+ :Variables:
+ """
+
+ if vb_idx == 0:
+ tds.all_vb_str = 'varbinds:'
+
+ tds.all_vb_str = tds.all_vb_str + " [" + str(vb_idx) + "] " \
+ + str(vb_oid) + " {" + vb_type + "} " + str(vb_val.prettyPrint())
+
+ # try:
+ # tds.all_vb_str = tds.all_vb_str + " [" + str(vb_idx) + "] " + vb_oid + " {" + vb_type + "} " + vb_val
+ # return 0
+ # except Exception as e:
+ # msg = "unable to add varbind to log string: %s" % (str(e))
+ # stdout_logger(msg)
+ # ecomp_logger(tds.LOG_TYPE_ERROR, tds.SEV_WARN, tds.CODE_GENERAL, msg)
+ # return 1
+
def add_varbind_to_json(vb_idx, vb_oid, vb_type, vb_val):
"""
Called for each varbind, adds individual attributes of varbind instance to
@@ -509,6 +580,7 @@ def add_varbind_to_json(vb_idx, vb_oid, vb_type, vb_val):
:Variables:
"""
+ agent_override_oid = ".1.3.6.1.6.3.18.1.3.0"
_individual_vb_dict = {}
# if first varbind (sysUptime), always return immediately as
@@ -516,10 +588,21 @@ def add_varbind_to_json(vb_idx, vb_oid, vb_type, vb_val):
if vb_idx == 0:
return 0
+ _vb_oid = "." + str(vb_oid.prettyPrint())
+ _vb_value = vb_val.prettyPrint()
+ _vb_type = pysnmp_to_netsnmp_varbind_convert(vb_type)
+
# if second varbind, use as notifyOID for all snmp versions
if vb_idx == 1:
- tds.trap_dict["notify OID"] = "." + str(vb_val.prettyPrint())
- tds.trap_dict["notify OID len"] = tds.trap_dict["notify OID"].count('.')
+ tds.trap_dict["notify OID"] = "." + _vb_value
+ tds.trap_dict["notify OID len"] = tds.trap_dict["notify OID"].count(
+ '.')
+ return 0
+
+ # if override varbind OID, use value as agent address
+ if _vb_oid == agent_override_oid:
+ tds.trap_dict["agent address"] = _vb_value
+ tds.trap_dict["agent name"] = resolve_ip(_vb_value)
return 0
# for SNMPv1 traps, skip varbinds 2, 3 and 4:
@@ -529,7 +612,7 @@ def add_varbind_to_json(vb_idx, vb_oid, vb_type, vb_val):
if tds.trap_dict["protocol version"] == "v1":
if vb_idx < 5:
return 0
-
+
if tds.first_varbind:
tds.all_vb_json_str = ', \"varbinds\": ['
tds.first_varbind = False
@@ -537,9 +620,9 @@ def add_varbind_to_json(vb_idx, vb_oid, vb_type, vb_val):
tds.all_vb_json_str = tds.all_vb_json_str + " ,"
_individual_vb_dict.clear()
- _individual_vb_dict['varbind_oid'] = "." + vb_oid.prettyPrint()
- _individual_vb_dict['varbind_type'] = pysnmp_to_netsnmp_varbind_convert(vb_type)
- _individual_vb_dict['varbind_value'] = vb_val.prettyPrint()
+ _individual_vb_dict['varbind_oid'] = _vb_oid
+ _individual_vb_dict['varbind_type'] = _vb_type
+ _individual_vb_dict['varbind_value'] = _vb_value
_individual_vb_json_str = json.dumps(_individual_vb_dict)
@@ -570,7 +653,7 @@ def notif_receiver_cb(snmp_engine, stateReference, contextEngineId, contextName,
:Variables:
"""
msg = "processing varbinds for %s" % (tds.trap_dict["uuid"])
- ecomp_logger(tds.LOG_TYPE_DEBUG, tds.SEV_DETAILED, tds.CODE_GENERAL, msg)
+ ecomp_logger(tds.LOG_TYPE_DEBUG, tds.SEV_INFO, tds.CODE_GENERAL, msg)
# help(snmp_engine)
# print(snmp_engine)
@@ -582,16 +665,20 @@ def notif_receiver_cb(snmp_engine, stateReference, contextEngineId, contextName,
# print(key, val)
# FMDL update reset location when batching publishes
- pdu_varbinds = 0
+ pdu_varbind_count = 0
payload_varbinds = 0
tds.all_vb_json_str = ""
+ tds.all_vb_str = " varbinds:"
tds.first_varbind = True
# iterate over varbinds, add to json struct
for vb_oid, vb_val in varBinds:
- varbinds_added = add_varbind_to_json(pdu_varbinds, vb_oid, vb_val.__class__.__name__, vb_val)
+ log_ret = add_varbind_to_log_string(
+ pdu_varbind_count, vb_oid, vb_val.__class__.__name__, vb_val)
+ varbinds_added = add_varbind_to_json(
+ pdu_varbind_count, vb_oid, vb_val.__class__.__name__, vb_val)
payload_varbinds += varbinds_added
- pdu_varbinds += 1
+ pdu_varbind_count += 1
curr_trap_json_str = json.dumps(tds.trap_dict)
# now have everything except varbinds in "curr_trap_json_str"
@@ -611,7 +698,7 @@ def notif_receiver_cb(snmp_engine, stateReference, contextEngineId, contextName,
curr_trap_json_str = curr_trap_json_str + '}'
msg = "trap %s : %s" % (tds.trap_dict["uuid"], curr_trap_json_str)
- ecomp_logger(tds.LOG_TYPE_DEBUG, tds.SEV_DETAILED, tds.CODE_GENERAL, msg)
+ ecomp_logger(tds.LOG_TYPE_DEBUG, tds.SEV_INFO, tds.CODE_GENERAL, msg)
# always log arriving traps
log_all_arriving_traps()
@@ -620,29 +707,41 @@ def notif_receiver_cb(snmp_engine, stateReference, contextEngineId, contextName,
tds.traps_since_last_publish += 1
milliseconds_since_last_publish = (time.time() - tds.last_pub_time) * 1000
- msg = "adding %s to buffer" % (tds.trap_dict["uuid"])
- ecomp_logger(tds.LOG_TYPE_DEBUG, tds.SEV_DETAILED, tds.CODE_GENERAL, msg)
- if tds.first_trap:
- tds.all_traps_str = curr_trap_json_str
- tds.trap_uuids_in_buffer = tds.trap_dict["uuid"]
- tds.first_trap = False
+ # only add to publish buffer if stormwatch is NOT active
+ if stormwatch.sw_storm_active(tds.trap_dict["agent address"], tds.trap_dict["notify OID"]):
+ msg = "stormwatch active - deflecting notification %s from %s" % (
+ tds.trap_dict["notify OID"], tds.trap_dict["agent address"])
+ ecomp_logger(tds.LOG_TYPE_DEBUG, tds.SEV_INFO,
+ tds.CODE_GENERAL, msg)
else:
- tds.trap_uuids_in_buffer = tds.trap_uuids_in_buffer + \
- ', ' + tds.trap_dict["uuid"]
- tds.all_traps_str = tds.all_traps_str + ', ' + curr_trap_json_str
+ msg = "adding %s to buffer" % (tds.trap_dict["uuid"])
+ ecomp_logger(tds.LOG_TYPE_DEBUG, tds.SEV_INFO,
+ tds.CODE_GENERAL, msg)
+ if tds.first_trap:
+ tds.all_traps_json_str = curr_trap_json_str
+ tds.trap_uuids_in_buffer = tds.trap_dict["uuid"]
+ tds.first_trap = False
+ else:
+ tds.trap_uuids_in_buffer = tds.trap_uuids_in_buffer + \
+ ', ' + tds.trap_dict["uuid"]
+ tds.all_traps_json_str = tds.all_traps_json_str + ', ' + curr_trap_json_str
- # publish to dmaap after last varbind is processed
- if tds.traps_since_last_publish >= tds.c_config['publisher']['max_traps_between_publishes']:
+ if tds.traps_since_last_publish >= int(tds.c_config['publisher']['max_traps_between_publishes']):
msg = "num traps since last publish (%d) exceeds threshold (%d) - publish traps" % (
- tds.traps_since_last_publish, tds.c_config['publisher']['max_traps_between_publishes'])
- ecomp_logger(tds.LOG_TYPE_DEBUG, tds.SEV_DETAILED,
+ tds.traps_since_last_publish, int(tds.c_config['publisher']['max_traps_between_publishes']))
+ ecomp_logger(tds.LOG_TYPE_DEBUG, tds.SEV_INFO,
tds.CODE_GENERAL, msg)
post_dmaap()
- elif milliseconds_since_last_publish >= tds.c_config['publisher']['max_milliseconds_between_publishes']:
+ elif milliseconds_since_last_publish >= int(tds.c_config['publisher']['max_milliseconds_between_publishes']):
msg = "num milliseconds since last publish (%.0f) exceeds threshold - publish traps" % milliseconds_since_last_publish
- ecomp_logger(tds.LOG_TYPE_DEBUG, tds.SEV_DETAILED,
+ ecomp_logger(tds.LOG_TYPE_DEBUG, tds.SEV_INFO,
tds.CODE_GENERAL, msg)
post_dmaap()
+ else:
+ msg = "neither milliseconds_since_last_publish (%.0f) or traps_since_last_publish (%d) exceed threshold - continue" % (
+ milliseconds_since_last_publish, tds.traps_since_last_publish)
+ ecomp_logger(tds.LOG_TYPE_DEBUG, tds.SEV_INFO,
+ tds.CODE_GENERAL, msg)
# # # # # # # # # # # # #
@@ -658,37 +757,39 @@ if __name__ == "__main__":
help="verbose logging")
parser.add_argument('-?', action="store_true", dest="usage_requested",
help="show command line use")
-
+
# parse args
args = parser.parse_args()
-
+
# set vars from args
verbose = args.verbose
usage_requested = args.usage_requested
-
+
# if usage, just display and exit
if usage_requested:
usage_err()
-
+
# init vars
tds.init()
-
+ sws.init()
+ stats.init()
+
# FMDL: add with stormWatch
# init sw vars
- # sw.init()
-
+ stormwatch.sw_init()
+
# Set initial startup hour for rolling logfile
tds.last_hour = datetime.datetime.now().hour
-
+
# get config binding service (CBS) values (either broker, or json file override)
load_all_configs(0, 0)
msg = "%s : %s version %s starting" % (
prog_name, tds.c_config['snmptrapd']['title'], tds.c_config['snmptrapd']['version'])
stdout_logger(msg)
-
+
# open various ecomp logs
open_eelf_logs()
-
+
# bump up logging level if overridden at command line
if verbose:
msg = "WARNING: '-v' argument present. All diagnostic messages will be logged. This can slow things down, use only when needed."
@@ -698,7 +799,6 @@ if __name__ == "__main__":
# debug.setLogger(debug.Debug('dsp', 'msgproc'))
debug.setLogger(debug.Debug('all'))
-
# name and open arriving trap log
tds.arriving_traps_filename = tds.c_config['files']['runtime_base_dir'] + "/" + \
tds.c_config['files']['log_dir'] + "/" + \
@@ -707,7 +807,7 @@ if __name__ == "__main__":
msg = ("arriving traps logged to: %s" % tds.arriving_traps_filename)
stdout_logger(msg)
ecomp_logger(tds.LOG_TYPE_DEBUG, tds.SEV_INFO, tds.CODE_GENERAL, msg)
-
+
# name and open json trap log
tds.json_traps_filename = tds.c_config['files']['runtime_base_dir'] + "/" + tds.c_config['files']['log_dir'] + "/" + "DMAAP_" + (
tds.c_config['streams_publishes']['sec_fault_unsecure']['dmaap_info']['topic_url'].split('/')[-1]) + ".json"
@@ -715,38 +815,38 @@ if __name__ == "__main__":
msg = ("published traps logged to: %s" % tds.json_traps_filename)
stdout_logger(msg)
ecomp_logger(tds.LOG_TYPE_DEBUG, tds.SEV_INFO, tds.CODE_GENERAL, msg)
-
+
# setup signal handling for config reload
signal.signal(signal.SIGUSR1, load_all_configs)
-
+
# save current PID for future/external reference
tds.pid_file_name = tds.c_config['files']['runtime_base_dir'] + \
'/' + tds.c_config['files']['pid_dir'] + '/' + prog_name + ".pid"
msg = "Runtime PID file: %s" % tds.pid_file_name
ecomp_logger(tds.LOG_TYPE_DEBUG, tds.SEV_INFO, tds.CODE_GENERAL, msg)
rc = save_pid(tds.pid_file_name)
-
+
# Get the event loop for this thread
loop = asyncio.get_event_loop()
-
+
# Create SNMP engine with autogenerated engineID pre-bound
# to socket transport dispatcher
snmp_engine = engine.SnmpEngine()
-
+
# # # # # # # # # # # #
# Transport setup
# # # # # # # # # # # #
-
+
# UDP over IPv4
try:
ipv4_interface = tds.c_config['protocols']['ipv4_interface']
- ipv4_port = tds.c_config['protocols']['ipv4_port']
-
+ ipv4_port = int(tds.c_config['protocols']['ipv4_port'])
+
try:
# FIXME: this doesn't appear to throw an exception even if
# the userID is unable (perms) to bind to port
#
- # We may need to open raw port using other
+ # We may need to open raw port using other
# means to confirm proper privileges (then
# close it and reopen w/ pysnmp api)
config.addTransport(
@@ -756,23 +856,23 @@ if __name__ == "__main__":
(ipv4_interface, ipv4_port))
)
except Exception as e:
- msg = "Unable to bind to %s:%s - %s" % (
+ msg = "Unable to bind to %s:%d - %s" % (
ipv4_interface, ipv4_port, str(e))
- ecomp_logger(tds.LOG_TYPE_ERROR, tds.SEV_FATAL, tds.CODE_GENERAL, msg)
+ ecomp_logger(tds.LOG_TYPE_ERROR, tds.SEV_FATAL,
+ tds.CODE_GENERAL, msg)
stdout_logger(msg)
cleanup_and_exit(1, tds.pid_file_name)
-
+
except Exception as e:
msg = "IPv4 interface and/or port not specified in config - not listening for IPv4 traps"
stdout_logger(msg)
ecomp_logger(tds.LOG_TYPE_ERROR, tds.SEV_WARN, tds.CODE_GENERAL, msg)
-
-
+
# UDP over IPv6
try:
ipv6_interface = tds.c_config['protocols']['ipv6_interface']
- ipv6_port = tds.c_config['protocols']['ipv6_port']
-
+ ipv6_port = int(tds.c_config['protocols']['ipv6_port'])
+
try:
config.addTransport(
snmp_engine,
@@ -781,27 +881,27 @@ if __name__ == "__main__":
(ipv6_interface, ipv6_port))
)
except Exception as e:
- msg = "Unable to bind to %s:%s - %s" % (
+ msg = "Unable to bind to %s:%d - %s" % (
ipv6_interface, ipv6_port, str(e))
stdout_logger(msg)
- ecomp_logger(tds.LOG_TYPE_ERROR, tds.SEV_FATAL, tds.CODE_GENERAL, msg)
+ ecomp_logger(tds.LOG_TYPE_ERROR, tds.SEV_FATAL,
+ tds.CODE_GENERAL, msg)
cleanup_and_exit(1, tds.pid_file_name)
-
+
except Exception as e:
msg = "IPv6 interface and/or port not specified in config - not listening for IPv6 traps"
stdout_logger(msg)
ecomp_logger(tds.LOG_TYPE_ERROR, tds.SEV_WARN, tds.CODE_GENERAL, msg)
-
-
+
# # # # # # # # # # # #
# SNMPv1/2c setup
# # # # # # # # # # # #
-
+
# SecurityName <-> CommunityName mapping
# to restrict trap reception to only those with specific community
# strings
config.addV1System(snmp_engine, 'my-area', 'public')
-
+
# register comm_string_rewrite_observer for message arrival
snmp_engine.observer.registerObserver(
comm_string_rewrite_observer,
@@ -811,7 +911,8 @@ if __name__ == "__main__":
# # # # # # # # # # # #
# SNMPv3 setup
# # # # # # # # # # # #
- config, snmp_engine=load_snmpv3_credentials(config, snmp_engine, tds.c_config)
+ config, snmp_engine = load_snmpv3_credentials(
+ config, snmp_engine, tds.c_config)
# register snmp_engine_observer_cb for message arrival
snmp_engine.observer.registerObserver(
@@ -819,12 +920,12 @@ if __name__ == "__main__":
'rfc3412.receiveMessage:request',
'rfc3412.returnResponsePdu',
)
-
+
# Register SNMP Application at the SNMP engine
ntfrcv.NotificationReceiver(snmp_engine, notif_receiver_cb)
-
+
snmp_engine.transportDispatcher.jobStarted(1) # loop forever
-
+
# Run I/O dispatcher which will receive traps
try:
snmp_engine.transportDispatcher.runDispatcher()
diff --git a/snmptrap/snmptrapd.sh b/snmptrap/snmptrapd.sh
index ab70c91..a10e049 100755
--- a/snmptrap/snmptrapd.sh
+++ b/snmptrap/snmptrapd.sh
@@ -1,47 +1,68 @@
#!/usr/bin/env bash
#
# ============LICENSE_START=======================================================
-# org.onap.dcae
-# ================================================================================
-# Copyright (c) 2017-2018 AT&T Intellectual Property. All rights reserved.
+# Copyright (c) 2017-2020 AT&T Intellectual Property. All rights reserved.
# ================================================================================
# 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.
# ============LICENSE_END=========================================================
-#
-# ECOMP is a trademark and service mark of AT&T Intellectual Property.
-#
# basics
current_cmd=`basename $0`
current_module=`echo $current_cmd | cut -d"." -f1`
-# FMDL:: need to pick up these values from json config, but it isn't
-# present at startup
-base_dir=/opt/app/snmptrap
-pid_file=${base_dir}/tmp/${current_module}.py.pid
+# get base_dir from current script invocation
+bin_base_dir="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )"
+base_dir=`dirname ${bin_base_dir}`
+
+snmptrapd_pid_file=${base_dir}/tmp/${current_module}.py.pid
+scheduler_pid_file=${base_dir}/tmp/scheduler.sh.pid
+
start_dir=${base_dir}/bin
+# global return code
+# - required because functions ultimately call log_msg, which
+# is to stdout, which conflicts with "echo <return_value>"
+# in the functions themselves
+g_return=0
+
# include path to 3.6+ version of python that has required dependencies included
export PATH=/opt/app/python-3.6.1/bin:$PATH
# set location of SSL certificates
-export REQUESTS_CA_BUNDLE=/etc/ssl/certs/ca-bundle.crt
+# export REQUESTS_CA_BUNDLE=/etc/ssl/certs/ca-bundle.crt # open source/external world
+export REQUESTS_CA_BUNDLE=/etc/ssl/certs/ca-certificates.crt # otherwise...
-# get to where we are supposed to be for startup
-cd /opt/app/snmptrap/bin
+# find the best tool for the job
+if [ `command -v pypy3` ]
+then
+ PY_BINARY=pypy3
+else
+ if [ `command -v python3` ]
+ then
+ PY_BINARY=python3
+ else
+ if [ `command -v python` ]
+ then
+ PY_BINARY=python
+ else
+ echo "ERROR: no pypy3 or python available in container - FATAL ERROR, exiting"
+ exit 1
+ fi
+ fi
+fi
# expand search for python modules to include ./mod in current/runtime dir
-export PYTHONPATH=./mod:./:$PYTHONPATH
+export PYTHONPATH=${bin_base_dir}/mod:$PYTHONPATH
# PYTHONUNBUFFERED:
# set PYTHONUNBUFFERED to a non-empty string to avoid output buffering;
@@ -50,7 +71,10 @@ export PYTHONPATH=./mod:./:$PYTHONPATH
# set location of config broker server overrride IF NEEDED
#
-export CBS_SIM_JSON=../etc/snmptrapd.json
+export CBS_SIM_JSON=${base_dir}/etc/snmptrapd.json
+
+# misc
+exit_after=1
# # # # # # # # # #
# log_msg - log messages to stdout in standard manner
@@ -59,7 +83,43 @@ log_msg()
{
msg=$*
- printf "`date +%Y-%m-%dT%H:%M:%S,%N | cut -c1-23` ${msg}"
+ echo "`date +%Y-%m-%dT%H:%M:%S,%N | cut -c1-23` ${msg}"
+}
+
+#
+# start process
+#
+start_process()
+{
+process_name=$1
+pid_file=$2
+exec_cmd=$3
+
+ # check if exec_cmd has a pid_file
+ if [ ! -r ${pid_file} ]
+ then
+ log_msg "Starting ${process_name}"
+ stdout_fd=${base_dir}/logs/${process_name}.out
+ if [ -f ${stdout_fd} ]
+ then
+ mv -f ${stdout_fd} ${stdout_fd}.bak
+ fi
+ ${exec_cmd} >> ${base_dir}/logs/${process_name}.out 2>&1 &
+ g_return=$?
+ echo $! > ${pid_file}
+ else
+ pid=$(cat ${pid_file})
+ if ps -p ${pid} > /dev/null
+ then
+ g_return=$?
+ log_msg "${process_name} already running - PID ${pid}"
+ else
+ log_msg "PID file present, but no corresponding process. Starting ${process_name}"
+ ${exec_cmd} >> ${base_dir}/logs/${process_name}.out 2>&1 &
+ g_return=$?
+ echo $! > ${pid_file}
+ fi
+ fi
}
# # # # # # # # # #
@@ -69,113 +129,142 @@ start_service()
{
# Hints for startup modifications:
# _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
+
+ # handy for debug (e.g. docker logs output)
+ # log_msg "Runtime env present for ${current_module} placed in ${base_dir}/logs/${current_module}.out"
+ env >> ${base_dir}/logs/${current_module}.out
+
# standard startup? Use this:
- cmd="python ./snmptrapd.py"
+ cmd="${PY_BINARY} ${base_dir}/bin/snmptrapd.py"
# want tracing? Use this:
- # cmd="python ./snmptrapd.py -v"
+ # cmd="${PY_BINARY} ./snmptrapd.py -v"
# unbuffered io for logs? Use this:
- # cmd="python -u ./snmptrapd.py"
+ # cmd="${PY_BINARY} -u ./snmptrapd.py"
# fmdl: needs further research
- # cmd="python -m trace --trackcalls ./snmptrapd.py"
+ # cmd="${PY_BINARY} -m trace --trackcalls ./snmptrapd.py"
cd ${start_dir}
- # check for process already running
- if [ -r ${pid_file} ]
+ #
+ # scheduler
+ #
+ start_process scheduler ${scheduler_pid_file} "${bin_base_dir}/scheduler.sh"
+ if [ ${g_return} -ne 0 ]
then
- pid=$(cat ${pid_file})
- if ps -p ${pid} > /dev/null
- then
- printf "${current_module} already running - PID ${pid}\n"
- return 0
- fi
+ log_msg "ERROR! Unable to start scheduler. Check logs for details."
fi
- # FMDL:: do this in snmptrapd.py at startup
- # roll log if present at startup
- # if [ -f ${LOGFILE} ]
- # then
- # mv -f ${LOGFILE} ${LOGFILE}.`date +%h-%m-%Y_%H:%M:%S`
- # fi
-
- log_msg "Starting ${current_module}... "
- eval ${cmd}
- return_code=$?
-
- if [ ${return_code} -eq 0 ]
+ #
+ # snmptrapd
+ #
+ start_process ${current_module} ${snmptrapd_pid_file} "${cmd}"
+ if [ ${g_return} -ne 0 ]
then
- log_msg "Started.\n"
- else
- log_msg "\nERROR! Unable to start ${current_module}. Check logs for details.\n"
+ log_msg "ERROR! Unable to start ${current_module}. Check logs for details."
fi
-
- return ${return_code}
-
}
# # # # # # # # # #
# Stop the service
# # # # # # # # # #
-stop_service()
+stop_process()
{
+process_name=$1
+pid_file=$2
+
if [ ! -r ${pid_file} ]
then
- log_msg "PID file ${pid_file} does not exist or not readable - unable to stop specific instance of ${current_module}.\n"
- log_msg "Diagnose further at command line as needed.\n"
- return_code=1
+ log_msg "PID file ${pid_file} does not exist or not readable - unable to stop ${process_name}"
+ g_return=1
else
pid=$(cat ${pid_file})
- log_msg "Stopping ${current_module} PID ${pid}...\n"
- kill ${pid}
- if [ $? -ne 0 ]
+ pgrep -f ${process_name} | grep "^${pid}" > /dev/null
+ loc_return=$?
+ if [ ${loc_return} -eq 0 ]
then
- log_msg "\nERROR while trying to terminate ${current_module} PID ${pid} (is it not running or owned by another userID?)"
- log_msg "\nDiagnose further at command line as needed."
- return_code=$?
- if [ -w ${pid_file} ]
+ log_msg "Stopping ${process_name} PID ${pid}..."
+ kill ${pid}
+ g_return=$?
+ if [ ${g_return} -eq 0 ]
then
- rm -f ${pid_file}
+ log_msg "Stopped"
+ else
+ log_msg "ERROR while terminating ${process_name} PID ${pid} (is it not running or owned by another userID?)"
fi
else
- log_msg "Stopped\n"
- if [ -w ${pid_file} ]
- then
- rm -f ${pid_file}
- fi
- return_code=0
+ log_msg "${process_name} PID ${pid} not present - skipping"
+ fi
+
+ if [ -w ${pid_file} ]
+ then
+ rm -f ${pid_file}
fi
fi
+}
+
+# # # # # # # # # # # # # # #
+# stop all snmptrapd services
+# # # # # # # # # # # # # # #
+stop_service()
+{
+ # scheduler
+ #
+ stop_process scheduler ${scheduler_pid_file}
- return ${return_code}
+ # snmptrapd
+ #
+ stop_process ${current_module} ${snmptrapd_pid_file}
}
# # # # # # # # # # # # # # #
# Check status of the service
# # # # # # # # # # # # # # #
-status_service()
+status_process()
{
+process_name=$1
+pid_file=$2
+
if [ -r ${pid_file} ]
then
pid=$(cat ${pid_file})
- pgrep -f ${current_module}.py | grep "^${pid}" > /dev/null
- return_code=$?
+ pgrep -f ${process_name} | grep "^${pid}" > /dev/null
+ loc_return=$?
- if [ ${return_code} -eq 0 ]
+ if [ ${loc_return} -eq 0 ]
then
- log_msg "Status: ${current_module} running\n"
- ps -f -p ${pid} -f | grep -v PID
- return_code=0
+ log_msg "- ${process_name} running, PID ${pid}"
+ # ps -f -p ${pid} -f | grep -v PID
+ g_return=0
else
- log_msg "Status: ERROR! ${current_module} not running.\n"
- return_code=1
+ log_msg "ERROR! ${process_name} not running"
+ g_return=1
fi
else
- log_msg "PID file ${pid_file} does not exist or not readable - unable to check status of ${current_module}\n"
- log_msg "Diagnose further at command line as needed.\n"
- return 1
+ log_msg "PID file ${pid_file} does not exist or not readable - unable to check status of ${process_name}"
+ g_return=1
fi
+}
- return ${return_code}
+#
+#
+#
+status_service()
+{
+ # scheduler
+ #
+ status_process scheduler ${scheduler_pid_file}
+ loc_return=${g_return}
+
+ # snmptrapd
+ #
+ status_process ${current_module} ${snmptrapd_pid_file}
+ loc_return=$((loc_return+g_return))
+ if [ ${loc_return} -ne 0 ]
+ then
+ log_msg "Overall Status: CRITICAL - Required process(es) missing!"
+ else
+ log_msg "Overall Status: Normal"
+ fi
}
# # # # # # # # # # # # # # # # #
@@ -183,62 +272,95 @@ status_service()
# # # # # # # # # # # # # # # # #
reload_cfg()
{
- if [ -r ${pid_file} ]
+ # only needed for snmptrapd
+ if [ -r ${snmptrapd_pid_file} ]
then
- pid=$(cat ${pid_file})
+ pid=$(cat ${snmptrapd_pid_file})
ps -p ${pid} > /dev/null 2>&1
- ret=$?
- if [ ${ret} ]
+ loc_return=$?
+ if [ ${loc_return} ]
then
- log_msg "Signaling ${current_module} PID ${pid} to request/read updated configs...\n"
+ log_msg "Signaling ${current_module} PID ${pid} to request/read updated configs..."
kill -USR1 ${pid}
- return_code=$?
- if [ ${return_code} -eq 0 ]
+ g_return=$?
+ if [ ${g_return} -eq 0 ]
then
- log_msg "...Signal complete.\n"
+ log_msg "...Signal complete."
else
- log_msg "\nERROR signaling ${current_module} - diagnose further at the command line.\n"
+ log_msg "ERROR signaling ${current_module} (do you have permissions to do this?)"
fi
else
- log_msg "\nERROR: ${current_module} PID ${pid} does not appear to be running.\n"
- return_code=1
+ log_msg "ERROR: ${current_module} PID ${pid} does not appear to be running."
fi
else
- log_msg "\nERROR: ${current_module} pid_file ${pid_file} does not exist - unable to signal for config re-read.\n"
- return_code=1
+ log_msg "ERROR: ${snmptrapd_pid_file} does not exist - unable to signal for config re-read."
+ g_return=1
fi
+}
+
+# # # # # # # # # # # # # # #
+# stop all snmptrapd services
+# # # # # # # # # # # # # # #
+version()
+{
+exit_swt=$1
+
+version_fd=${base_dir}/etc/version.dat
+if [ -f ${version_fd} ]
+then
+ version_string=`cat ${version_fd}`
+ log_msg "${version_string}"
+ ec=0
+else
+ log_msg "ERROR: unable to determine version"
+ ec=1
+fi
+
+if [ "${exit_swt}" == "${exit_after}" ]
+then
+ exit ${ec}
+fi
- return ${return_code}
}
# # # # # # # # # # # # #
# M A I N
# # # # # # # # # # # # #
+
case "$1" in
"start")
+ version
start_service
- exit $?
+ sleep 1
+ status_service
+ wait
;;
"stop")
+ version
stop_service
- exit $?
;;
"restart")
+ version
stop_service
sleep 1
start_service
- exit $?
+ status_service
;;
"status")
+ version
status_service
- exit $?
;;
"reloadCfg")
+ version
reload_cfg
- exit $?
+ ;;
+ "version")
+ version ${exit_after}
;;
*)
- printf "\nUsage: ${current_cmd} {start|stop|restart|status|rollLog|reloadCfg}\n"
- exit 1
+ echo "Usage: ${current_cmd} {start|stop|restart|status|reloadCfg|version}"
+ g_return=1
esac
+
+exit ${g_return}
diff --git a/tests/__init__.py b/tests/__init__.py
index 1875bf6..af0fbb2 100644
--- a/tests/__init__.py
+++ b/tests/__init__.py
@@ -1,5 +1,5 @@
# ================================================================================
-# Copyright (c) 2018 AT&T Intellectual Property. All rights reserved.
+# Copyright (c) 2018-2020 AT&T Intellectual Property. All rights reserved.
# ================================================================================
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
diff --git a/tests/py-test-snmptrap.sh b/tests/py-test-snmptrap.sh
new file mode 100755
index 0000000..b57eef6
--- /dev/null
+++ b/tests/py-test-snmptrap.sh
@@ -0,0 +1,26 @@
+#!/bin/env bash
+# ============LICENSE_START=======================================================
+# Copyright (c) 2020 AT&T Intellectual Property. All rights reserved.
+# ================================================================================
+# 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.
+# ============LICENSE_END=========================================================
+
+# export CBS_SIM_JSON=/opt/app/snmptrap/etc/snmptrapd.json
+export PYTHONPATH=/opt/app/snmptrap/bin:/opt/app/snmptrap/bin/mod:$PYTHONPATH
+
+# cd /opt/app/snmptrap
+cd /opt/app
+
+py.test --cov-config /opt/app/snmptrap/tests/.coveragerc --cov=snmptrap /opt/app/snmptrap/tests/
+# py.test --cov=snmptrap /opt/app/snmptrap/tests/
+coverage report -m
diff --git a/tests/test_snmptrapd.py b/tests/test_snmptrapd.py
index 9dc92c0..cdd19a4 100644
--- a/tests/test_snmptrapd.py
+++ b/tests/test_snmptrapd.py
@@ -1,3 +1,19 @@
+# ============LICENSE_START=======================================================
+# Copyright (c) 2018-2020 AT&T Intellectual Property. All rights reserved.
+# ================================================================================
+# 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.
+# ============LICENSE_END=========================================================
+
import os
import pytest
import unittest
@@ -5,6 +21,8 @@ import snmptrapd
import datetime
import trapd_settings as tds
+import trapd_stormwatch_settings as sws
+import trapd_stormwatch as sw
import trapd_http_session
import trapd_runtime_pid
import trapd_io
@@ -18,7 +36,7 @@ class test_snmptrapd(unittest.TestCase):
Test the save_pid mod
"""
- pytest_json_data = "{ \"snmptrapd\": { \"version\": \"1.4.0\", \"title\": \"ONAP SNMP Trap Receiver\" }, \"protocols\": { \"transport\": \"udp\", \"ipv4_interface\": \"0.0.0.0\", \"ipv4_port\": 6162, \"ipv6_interface\": \"::1\", \"ipv6_port\": 6162 }, \"cache\": { \"dns_cache_ttl_seconds\": 60 }, \"publisher\": { \"http_timeout_milliseconds\": 1500, \"http_retries\": 3, \"http_milliseconds_between_retries\": 750, \"http_primary_publisher\": \"true\", \"http_peer_publisher\": \"unavailable\", \"max_traps_between_publishes\": 10, \"max_milliseconds_between_publishes\": 10000 }, \"streams_publishes\": { \"sec_fault_unsecure\": { \"type\": \"message_router\", \"aaf_password\": null, \"dmaap_info\": { \"location\": \"mtl5\", \"client_id\": null, \"client_role\": null, \"topic_url\": \"http://uebsb91kcdc.it.att.com:3904/events/ONAP-COLLECTOR-SNMPTRAP\" }, \"aaf_username\": null } }, \"files\": { \"runtime_base_dir\": \"/tmp/opt/app/snmptrap\", \"log_dir\": \"logs\", \"data_dir\": \"data\", \"pid_dir\": \"tmp\", \"arriving_traps_log\": \"snmptrapd_arriving_traps.log\", \"snmptrapd_diag\": \"snmptrapd_prog_diag.log\", \"traps_stats_log\": \"snmptrapd_stats.csv\", \"perm_status_file\": \"snmptrapd_status.log\", \"eelf_base_dir\": \"/tmp/opt/app/snmptrap/logs\", \"eelf_error\": \"error.log\", \"eelf_debug\": \"debug.log\", \"eelf_audit\": \"audit.log\", \"eelf_metrics\": \"metrics.log\", \"roll_frequency\": \"day\", \"minimum_severity_to_log\": 2 }, \"trap_config\": { \"sw_interval_in_seconds\": 60, \"notify_oids\": { \".1.3.6.1.4.1.9.0.1\": { \"sw_high_water_in_interval\": 102, \"sw_low_water_in_interval\": 7, \"category\": \"logonly\" }, \".1.3.6.1.4.1.9.0.2\": { \"sw_high_water_in_interval\": 101, \"sw_low_water_in_interval\": 7, \"category\": \"logonly\" }, \".1.3.6.1.4.1.9.0.3\": { \"sw_high_water_in_interval\": 102, \"sw_low_water_in_interval\": 7, \"category\": \"logonly\" }, \".1.3.6.1.4.1.9.0.4\": { \"sw_high_water_in_interval\": 10, \"sw_low_water_in_interval\": 3, \"category\": \"logonly\" } } }, \"snmpv3_config\": { \"usm_users\": [ { \"user\": \"usr-sha-aes256\", \"engineId\": \"8000000001020304\", \"usmHMACSHAAuth\": \"authkey1\", \"usmAesCfb256\": \"privkey1\" }, { \"user\": \"user1\", \"engineId\": \"8000000000000001\", \"usmHMACMD5Auth\": \"authkey1\", \"usmDESPriv\": \"privkey1\" }, { \"user\": \"user2\", \"engineId\": \"8000000000000002\", \"usmHMACSHAAuth\": \"authkey2\", \"usmAesCfb128\": \"privkey2\" }, { \"user\": \"user3\", \"engineId\": \"8000000000000003\", \"usmHMACSHAAuth\": \"authkey3\", \"usmAesCfb256\": \"privkey3\" } ] } }"
+ pytest_json_data = "{ \"snmptrapd\": { \"version\": \"2.0.3\", \"title\": \"ONAP SNMP Trap Receiver\" }, \"protocols\": { \"transport\": \"udp\", \"ipv4_interface\": \"0.0.0.0\", \"ipv4_port\": 6162, \"ipv6_interface\": \"::1\", \"ipv6_port\": 6162 }, \"cache\": { \"dns_cache_ttl_seconds\": 60 }, \"publisher\": { \"http_timeout_milliseconds\": 1500, \"http_retries\": 3, \"http_milliseconds_between_retries\": 750, \"http_primary_publisher\": \"true\", \"http_peer_publisher\": \"unavailable\", \"max_traps_between_publishes\": 10, \"max_milliseconds_between_publishes\": 10000 }, \"streams_publishes\": { \"sec_fault_unsecure\": { \"type\": \"message_router\", \"aaf_password\": null, \"dmaap_info\": { \"location\": \"mtl5\", \"client_id\": null, \"client_role\": null, \"topic_url\": \"http://uebsb91kcdc.it.att.com:3904/events/ONAP-COLLECTOR-SNMPTRAP\" }, \"aaf_username\": null } }, \"files\": { \"runtime_base_dir\": \"/tmp/opt/app/snmptrap\", \"log_dir\": \"logs\", \"data_dir\": \"data\", \"pid_dir\": \"tmp\", \"arriving_traps_log\": \"snmptrapd_arriving_traps.log\", \"snmptrapd_diag\": \"snmptrapd_prog_diag.log\", \"traps_stats_log\": \"snmptrapd_stats.csv\", \"perm_status_file\": \"snmptrapd_status.log\", \"eelf_base_dir\": \"/tmp/opt/app/snmptrap/logs\", \"eelf_error\": \"error.log\", \"eelf_debug\": \"debug.log\", \"eelf_audit\": \"audit.log\", \"eelf_metrics\": \"metrics.log\", \"roll_frequency\": \"day\", \"minimum_severity_to_log\": 2 }, \"trap_config\": { \"sw_interval_in_seconds\": 60, \"notify_oids\": { \".1.3.6.1.4.1.9.0.1\": { \"sw_high_water_in_interval\": 102, \"sw_low_water_in_interval\": 7, \"category\": \"logonly\" }, \".1.3.6.1.4.1.9.0.2\": { \"sw_high_water_in_interval\": 101, \"sw_low_water_in_interval\": 7, \"category\": \"logonly\" }, \".1.3.6.1.4.1.9.0.3\": { \"sw_high_water_in_interval\": 102, \"sw_low_water_in_interval\": 7, \"category\": \"logonly\" }, \".1.3.6.1.4.1.9.0.4\": { \"sw_high_water_in_interval\": 10, \"sw_low_water_in_interval\": 3, \"category\": \"logonly\" } } }, \"snmpv3_config\": { \"usm_users\": [ { \"user\": \"usr-sha-aes256\", \"engineId\": \"8000000001020304\", \"usmHMACSHAAuth\": \"authkey1\", \"usmAesCfb256\": \"privkey1\" }, { \"user\": \"user1\", \"engineId\": \"8000000000000001\", \"usmHMACMD5Auth\": \"authkey1\", \"usmDESPriv\": \"privkey1\" }, { \"user\": \"user2\", \"engineId\": \"8000000000000002\", \"usmHMACSHAAuth\": \"authkey2\", \"usmAesCfb128\": \"privkey2\" }, { \"user\": \"user3\", \"engineId\": \"8000000000000003\", \"usmHMACSHAAuth\": \"authkey3\", \"usmAesCfb256\": \"privkey3\" } ] } }"
# create copy of snmptrapd.json for pytest
pytest_json_config = "/tmp/opt/app/snmptrap/etc/snmptrapd.json"
@@ -44,6 +62,7 @@ class test_snmptrapd(unittest.TestCase):
# init vars
tds.init()
+ sw.sw_init()
# request load of CBS data
os.environ.update(CBS_SIM_JSON='/tmp/opt/app/snmptrap/etc/snmptrapd.json')
@@ -150,29 +169,5 @@ class test_snmptrapd(unittest.TestCase):
result = errorIndication
self.assertEqual(result, None)
- def test_add_varbind_to_json(self):
-
- # init vars
- tds.init()
- tds.trap_dict["notify OID"] = ".1.2.3.4.5.6.7.8"
- tds.trap_dict["protocol version"] = "v2c"
-
- # varbinds=[(ObjectName('1.3.6.1.2.1.1.3.0'), TimeTicks(0)), (ObjectName('1.3.6.1.6.3.1.1.4.1.0'), ObjectIdentifier('1.3.6.1.4.1.74.2.46.12.1.1')), (ObjectName('1.3.6.1.4.1.74.2.46.12.1.1.1'), OctetString(b'ucsnmp heartbeat - ignore')), (ObjectName('1.3.6.1.4.1.74.2.46.12.1.1.2'), OctetString(b'Thu Mar 21 15:46:58 2019'))]
-
- # vb=(ObjectName('1.3.6.1.4.1.74.2.46.12.1.1.1'), OctetString(b'ucsnmp heartbeat - ignore'))
-
- self.assertEqual(snmptrapd.add_varbind_to_json(0,ObjectIdentifier('.1.2.3.4'), 'OctetString', OctetString(b'Thu Mar 21 15:46:58 2019')), 0)
- self.assertEqual(snmptrapd.add_varbind_to_json(1,ObjectIdentifier('.1.2.3.4'), 'OctetString', OctetString(b'Thu Mar 21 15:46:58 2019')), 0)
- self.assertEqual(snmptrapd.add_varbind_to_json(2,ObjectIdentifier('.1.2.3.4'), 'OctetString', OctetString(b'Thu Mar 21 15:46:58 2019')), 1)
- self.assertEqual(snmptrapd.add_varbind_to_json(3,ObjectIdentifier('.1.2.3.4'), 'OctetString', OctetString(b'Thu Mar 21 15:46:58 2019')), 1)
-
- # init vars
- tds.init()
- tds.trap_dict["notify OID"] = ".1.2.3.4.5.6.7.8"
- tds.trap_dict["protocol version"] = "v1"
-
- self.assertEqual(snmptrapd.add_varbind_to_json(0,ObjectIdentifier('.1.2.3.4'), 'OctetString', OctetString(b'Thu Mar 21 15:46:58 2019')), 0)
- self.assertEqual(snmptrapd.add_varbind_to_json(5,ObjectIdentifier('.1.2.3.4'), 'OctetString', OctetString(b'Thu Mar 21 15:46:58 2019')), 1)
-
if __name__ == '__main__':
unittest.main()
diff --git a/tests/test_snmptrapd_send_test_trap.py b/tests/test_snmptrapd_send_test_trap.py
index 54d522e..0e23def 100755
--- a/tests/test_snmptrapd_send_test_trap.py
+++ b/tests/test_snmptrapd_send_test_trap.py
@@ -1,3 +1,19 @@
+# ============LICENSE_START=======================================================
+# Copyright (c) 2018-2020 AT&T Intellectual Property. All rights reserved.
+# ================================================================================
+# 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.
+# ============LICENSE_END=========================================================
+
from pysnmp.hlapi import *
from pysnmp import debug
diff --git a/tests/test_trapd_exit.py b/tests/test_trapd_exit.py
index 594624f..3bbc386 100644
--- a/tests/test_trapd_exit.py
+++ b/tests/test_trapd_exit.py
@@ -1,3 +1,19 @@
+# ============LICENSE_START=======================================================
+# Copyright (c) 2018-2020 AT&T Intellectual Property. All rights reserved.
+# ================================================================================
+# 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.
+# ============LICENSE_END=========================================================
+
import pytest
import unittest
import trapd_exit
diff --git a/tests/test_trapd_get_cbs_config.py b/tests/test_trapd_get_cbs_config.py
index 44bf021..67bccfc 100644
--- a/tests/test_trapd_get_cbs_config.py
+++ b/tests/test_trapd_get_cbs_config.py
@@ -1,44 +1,77 @@
+# ============LICENSE_START=======================================================
+# Copyright (c) 2019-2020 AT&T Intellectual Property. All rights reserved.
+# ================================================================================
+# 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.
+# ============LICENSE_END=========================================================
+
import pytest
import unittest
import os
+import sys
from onap_dcae_cbs_docker_client.client import get_config
from trapd_exit import cleanup_and_exit
from trapd_io import stdout_logger, ecomp_logger
import trapd_settings as tds
import trapd_get_cbs_config
-
+
+from pathlib import Path
+
+# # # # # # # #
+# ENV setup
+# # # # # # # #
+
+# required directory tree
+try:
+ Path("/tmp/opt/app/snmptrap/logs").mkdir(parents=True, exist_ok=True)
+ Path("/tmp/opt/app/snmptrap/tmp").mkdir(parents=True, exist_ok=True)
+except Exception as e:
+ print("Error while running %s : %s" % (os.path.basename(__file__), str(e.strerror)))
+ sys.exit(1)
+
+# env var for CBS_SIM_JSON
+try:
+ os.environ['CBS_SIM_JSON'] = "/tmp/opt/app/snmptrap/etc/snmptrapd.json"
+except Exception as e:
+ print("Error while running %s : %s" % (os.path.basename(__file__), str(e.strerror)))
+ sys.exit(1)
+
+pytest_json_data = "{ \"snmptrapd\": { \"version\": \"1.4.0\", \"title\": \"ONAP SNMP Trap Receiver\" }, \"protocols\": { \"transport\": \"udp\", \"ipv4_interface\": \"0.0.0.0\", \"ipv4_port\": 6162, \"ipv6_interface\": \"::1\", \"ipv6_port\": 6162 }, \"cache\": { \"dns_cache_ttl_seconds\": 60 }, \"publisher\": { \"http_timeout_milliseconds\": 1500, \"http_retries\": 3, \"http_milliseconds_between_retries\": 750, \"http_primary_publisher\": \"true\", \"http_peer_publisher\": \"unavailable\", \"max_traps_between_publishes\": 10, \"max_milliseconds_between_publishes\": 10000 }, \"streams_publishes\": { \"sec_fault_unsecure\": { \"type\": \"message_router\", \"aaf_password\": null, \"dmaap_info\": { \"location\": \"mtl5\", \"client_id\": null, \"client_role\": null, \"topic_url\": \"http://localhost:3904/events/ONAP-COLLECTOR-SNMPTRAP\" }, \"aaf_username\": null } }, \"files\": { \"runtime_base_dir\": \"/tmp/opt/app/snmptrap\", \"log_dir\": \"logs\", \"data_dir\": \"data\", \"pid_dir\": \"tmp\", \"arriving_traps_log\": \"snmptrapd_arriving_traps.log\", \"snmptrapd_diag\": \"snmptrapd_prog_diag.log\", \"traps_stats_log\": \"snmptrapd_stats.csv\", \"perm_status_file\": \"snmptrapd_status.log\", \"eelf_base_dir\": \"/tmp/opt/app/snmptrap/logs\", \"eelf_error\": \"error.log\", \"eelf_debug\": \"debug.log\", \"eelf_audit\": \"audit.log\", \"eelf_metrics\": \"metrics.log\", \"roll_frequency\": \"day\", \"minimum_severity_to_log\": 2 }, \"trap_config\": { \"sw_interval_in_seconds\": 60, \"notify_oids\": { \".1.3.6.1.4.1.9.0.1\": { \"sw_high_water_in_interval\": 102, \"sw_low_water_in_interval\": 7, \"category\": \"logonly\" }, \".1.3.6.1.4.1.9.0.2\": { \"sw_high_water_in_interval\": 101, \"sw_low_water_in_interval\": 7, \"category\": \"logonly\" }, \".1.3.6.1.4.1.9.0.3\": { \"sw_high_water_in_interval\": 102, \"sw_low_water_in_interval\": 7, \"category\": \"logonly\" }, \".1.3.6.1.4.1.9.0.4\": { \"sw_high_water_in_interval\": 10, \"sw_low_water_in_interval\": 3, \"category\": \"logonly\" } } }, \"snmpv3_config\": { \"usm_users\": [ { \"user\": \"usr-sha-aes256\", \"engineId\": \"8000000001020304\", \"usmHMACSHAAuth\": \"authkey1\", \"usmAesCfb256\": \"privkey1\" }, { \"user\": \"user1\", \"engineId\": \"8000000000000001\", \"usmHMACMD5Auth\": \"authkey1\", \"usmDESPriv\": \"privkey1\" }, { \"user\": \"user2\", \"engineId\": \"8000000000000002\", \"usmHMACSHAAuth\": \"authkey2\", \"usmAesCfb128\": \"privkey2\" }, { \"user\": \"user3\", \"engineId\": \"8000000000000003\", \"usmHMACSHAAuth\": \"authkey3\", \"usmAesCfb256\": \"privkey3\" } ] } }"
+
+# create snmptrapd.json for pytest
+pytest_json_config = os.getenv('CBS_SIM_JSON')
+with open(pytest_json_config, 'w') as outfile:
+ outfile.write(pytest_json_data)
+outfile.close()
+
+# test class/methods
class test_get_cbs_config(unittest.TestCase):
"""
Test the trapd_get_cbs_config mod
"""
- pytest_json_data = "{ \"snmptrapd\": { \"version\": \"1.4.0\", \"title\": \"ONAP SNMP Trap Receiver\" }, \"protocols\": { \"transport\": \"udp\", \"ipv4_interface\": \"0.0.0.0\", \"ipv4_port\": 6162, \"ipv6_interface\": \"::1\", \"ipv6_port\": 6162 }, \"cache\": { \"dns_cache_ttl_seconds\": 60 }, \"publisher\": { \"http_timeout_milliseconds\": 1500, \"http_retries\": 3, \"http_milliseconds_between_retries\": 750, \"http_primary_publisher\": \"true\", \"http_peer_publisher\": \"unavailable\", \"max_traps_between_publishes\": 10, \"max_milliseconds_between_publishes\": 10000 }, \"streams_publishes\": { \"sec_fault_unsecure\": { \"type\": \"message_router\", \"aaf_password\": null, \"dmaap_info\": { \"location\": \"mtl5\", \"client_id\": null, \"client_role\": null, \"topic_url\": \"http://localhost:3904/events/ONAP-COLLECTOR-SNMPTRAP\" }, \"aaf_username\": null } }, \"files\": { \"runtime_base_dir\": \"/tmp/opt/app/snmptrap\", \"log_dir\": \"logs\", \"data_dir\": \"data\", \"pid_dir\": \"tmp\", \"arriving_traps_log\": \"snmptrapd_arriving_traps.log\", \"snmptrapd_diag\": \"snmptrapd_prog_diag.log\", \"traps_stats_log\": \"snmptrapd_stats.csv\", \"perm_status_file\": \"snmptrapd_status.log\", \"eelf_base_dir\": \"/tmp/opt/app/snmptrap/logs\", \"eelf_error\": \"error.log\", \"eelf_debug\": \"debug.log\", \"eelf_audit\": \"audit.log\", \"eelf_metrics\": \"metrics.log\", \"roll_frequency\": \"day\", \"minimum_severity_to_log\": 2 }, \"trap_config\": { \"sw_interval_in_seconds\": 60, \"notify_oids\": { \".1.3.6.1.4.1.9.0.1\": { \"sw_high_water_in_interval\": 102, \"sw_low_water_in_interval\": 7, \"category\": \"logonly\" }, \".1.3.6.1.4.1.9.0.2\": { \"sw_high_water_in_interval\": 101, \"sw_low_water_in_interval\": 7, \"category\": \"logonly\" }, \".1.3.6.1.4.1.9.0.3\": { \"sw_high_water_in_interval\": 102, \"sw_low_water_in_interval\": 7, \"category\": \"logonly\" }, \".1.3.6.1.4.1.9.0.4\": { \"sw_high_water_in_interval\": 10, \"sw_low_water_in_interval\": 3, \"category\": \"logonly\" } } }, \"snmpv3_config\": { \"usm_users\": [ { \"user\": \"usr-sha-aes256\", \"engineId\": \"8000000001020304\", \"usmHMACSHAAuth\": \"authkey1\", \"usmAesCfb256\": \"privkey1\" }, { \"user\": \"user1\", \"engineId\": \"8000000000000001\", \"usmHMACMD5Auth\": \"authkey1\", \"usmDESPriv\": \"privkey1\" }, { \"user\": \"user2\", \"engineId\": \"8000000000000002\", \"usmHMACSHAAuth\": \"authkey2\", \"usmAesCfb128\": \"privkey2\" }, { \"user\": \"user3\", \"engineId\": \"8000000000000003\", \"usmHMACSHAAuth\": \"authkey3\", \"usmAesCfb256\": \"privkey3\" } ] } }"
-
- # create copy of snmptrapd.json for pytest
- pytest_json_config = "/tmp/opt/app/snmptrap/etc/snmptrapd.json"
- with open(pytest_json_config, 'w') as outfile:
- outfile.write(pytest_json_data)
-
-
- def test_cbs_env_present(self):
+ def test_cbs_fallback_env_present(self):
"""
- Test that CONSUL_HOST env variable exists but fails to
- respond
+ Test that CBS fallback env variable exists and we can get config
+ from fallback env var
"""
- os.environ.update(CONSUL_HOST='nosuchhost')
- # del os.environ['CBS_SIM_JSON']
- # result = trapd_get_cbs_config.get_cbs_config()
- # print("result: %s" % result)
+ os.environ.update(CBS_SIM_JSON='/tmp/opt/app/snmptrap/etc/snmptrapd.json')
+ result = trapd_get_cbs_config.get_cbs_config()
+ print("result: %s" % result)
# compare = str(result).startswith("{'snmptrap': ")
- # self.assertEqual(compare, False)
-
- with pytest.raises(Exception) as pytest_wrapped_sys_exit:
- result = trapd_get_cbs_config.get_cbs_config()
- assert pytest_wrapped_sys_exit.type == SystemExit
- # assert pytest_wrapped_sys_exit.value.code == 1
+ # self.assertEqual(compare, True)
+ self.assertEqual(result, True)
-
def test_cbs_override_env_invalid(self):
"""
"""
@@ -51,35 +84,32 @@ class test_get_cbs_config(unittest.TestCase):
with pytest.raises(SystemExit) as pytest_wrapped_sys_exit:
result = trapd_get_cbs_config.get_cbs_config()
assert pytest_wrapped_sys_exit.type == SystemExit
- assert pytest_wrapped_sys_exit.value.code == 1
-
+ # assert pytest_wrapped_sys_exit.value.code == 1
- def test_cbs_override_env_unset(self):
+ def test_cbs_env_present(self):
"""
+ Test that CONSUL_HOST env variable exists but fails to
+ respond
"""
- os.environ.update(CBS_SIM_JSON='')
- #result = trapd_get_cbs_config.get_cbs_config()
- #print("result: %s" % result)
+ os.environ.update(CONSUL_HOST='localhost')
+ del os.environ['CBS_SIM_JSON']
+ # result = trapd_get_cbs_config.get_cbs_config()
+ # print("result: %s" % result)
# compare = str(result).startswith("{'snmptrap': ")
# self.assertEqual(compare, False)
- with pytest.raises(SystemExit) as pytest_wrapped_sys_exit:
- result = trapd_get_cbs_config.get_cbs_config()
- assert pytest_wrapped_sys_exit.type == SystemExit
- assert pytest_wrapped_sys_exit.value.code == 1
-
+ with pytest.raises(SystemExit) as sys_exit:
+ trapd_get_cbs_config.get_cbs_config()
+ assert sys_exit.value.errno == errno.ECONNREFUSED
- def test_cbs_fallback_env_present(self):
+ def test_cbs_override_env_undefined(self):
"""
- Test that CBS fallback env variable exists and we can get config
- from fallback env var
"""
- os.environ.update(CBS_SIM_JSON='/tmp/opt/app/snmptrap/etc/snmptrapd.json')
- result = trapd_get_cbs_config.get_cbs_config()
- print("result: %s" % result)
- # compare = str(result).startswith("{'snmptrap': ")
- # self.assertEqual(compare, True)
- self.assertEqual(result, True)
+ print("------>>> RUNNING test_no_cbs_override_env_var:")
+ del os.environ['CBS_SIM_JSON']
+
+ with pytest.raises(SystemExit) as pytest_wrapped_sys_exit:
+ assert trapd_get_cbs_config.get_cbs_config() == SystemExit
if __name__ == '__main__':
unittest.main()
diff --git a/tests/test_trapd_http_session.py b/tests/test_trapd_http_session.py
index a423be6..478b29d 100644
--- a/tests/test_trapd_http_session.py
+++ b/tests/test_trapd_http_session.py
@@ -1,3 +1,19 @@
+# ============LICENSE_START=======================================================
+# Copyright (c) 2019-2020 AT&T Intellectual Property. All rights reserved.
+# ================================================================================
+# 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.
+# ============LICENSE_END=========================================================
+
import pytest
import unittest
import trapd_http_session
@@ -7,6 +23,14 @@ class test_init_session_obj(unittest.TestCase):
Test the init_session_obj mod
"""
+ def close_nonexisting_session(self):
+ """
+ test close of existing http session
+ """
+ sess="no session"
+ result = trapd_http_session.close_session_obj(sess)
+ self.assertEqual(result, True)
+
def init_session(self):
"""
test creation of http session
@@ -32,12 +56,5 @@ class test_init_session_obj(unittest.TestCase):
result = trapd_http_session.close_session_obj(sess)
self.assertEqual(result, True)
- def close_nonexistent_session(self):
- """
- test close of non-existent http session
- """
- result = trapd_http_session.close_session_obj(None)
- self.assertEqual(result, True)
-
if __name__ == '__main__':
unittest.main()
diff --git a/tests/test_trapd_io.py b/tests/test_trapd_io.py
index 0ec93d5..3e32493 100644
--- a/tests/test_trapd_io.py
+++ b/tests/test_trapd_io.py
@@ -1,3 +1,19 @@
+# ============LICENSE_START=======================================================
+# Copyright (c) 2019-2020 AT&T Intellectual Property. All rights reserved.
+# ================================================================================
+# 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.
+# ============LICENSE_END=========================================================
+
import os
import pytest
import unittest
@@ -7,127 +23,176 @@ import json
import trapd_settings as tds
import trapd_runtime_pid
import trapd_io
+import sys
+from pathlib import Path
+
class test_trapd_io(unittest.TestCase):
"""
Test the save_pid mod
"""
tds.c_config = json.loads("{ \"snmptrapd\": { \"version\": \"1.4.0\", \"title\": \"ONAP SNMP Trap Receiver\" }, \"protocols\": { \"transport\": \"udp\", \"ipv4_interface\": \"0.0.0.0\", \"ipv4_port\": 6162, \"ipv6_interface\": \"::1\", \"ipv6_port\": 6162 }, \"cache\": { \"dns_cache_ttl_seconds\": 60 }, \"publisher\": { \"http_timeout_milliseconds\": 1500, \"http_retries\": 3, \"http_milliseconds_between_retries\": 750, \"http_primary_publisher\": \"true\", \"http_peer_publisher\": \"unavailable\", \"max_traps_between_publishes\": 10, \"max_milliseconds_between_publishes\": 10000 }, \"streams_publishes\": { \"sec_fault_unsecure\": { \"type\": \"message_router\", \"aaf_password\": null, \"dmaap_info\": { \"location\": \"mtl5\", \"client_id\": null, \"client_role\": null, \"topic_url\": \"http://localhost:3904/events/ONAP-COLLECTOR-SNMPTRAP\" }, \"aaf_username\": null } }, \"files\": { \"runtime_base_dir\": \"/tmp/opt/app/snmptrap\", \"log_dir\": \"logs\", \"data_dir\": \"data\", \"pid_dir\": \"tmp\", \"arriving_traps_log\": \"snmptrapd_arriving_traps.log\", \"snmptrapd_diag\": \"snmptrapd_prog_diag.log\", \"traps_stats_log\": \"snmptrapd_stats.csv\", \"perm_status_file\": \"snmptrapd_status.log\", \"eelf_base_dir\": \"/tmp/opt/app/snmptrap/logs\", \"eelf_error\": \"error.log\", \"eelf_debug\": \"debug.log\", \"eelf_audit\": \"audit.log\", \"eelf_metrics\": \"metrics.log\", \"roll_frequency\": \"day\", \"minimum_severity_to_log\": 2 }, \"trap_config\": { \"sw_interval_in_seconds\": 60, \"notify_oids\": { \".1.3.6.1.4.1.9.0.1\": { \"sw_high_water_in_interval\": 102, \"sw_low_water_in_interval\": 7, \"category\": \"logonly\" }, \".1.3.6.1.4.1.9.0.2\": { \"sw_high_water_in_interval\": 101, \"sw_low_water_in_interval\": 7, \"category\": \"logonly\" }, \".1.3.6.1.4.1.9.0.3\": { \"sw_high_water_in_interval\": 102, \"sw_low_water_in_interval\": 7, \"category\": \"logonly\" }, \".1.3.6.1.4.1.9.0.4\": { \"sw_high_water_in_interval\": 10, \"sw_low_water_in_interval\": 3, \"category\": \"logonly\" } } }, \"snmpv3_config\": { \"usm_users\": [ { \"user\": \"usr-sha-aes256\", \"engineId\": \"8000000001020304\", \"usmHMACSHAAuth\": \"authkey1\", \"usmAesCfb256\": \"privkey1\" }, { \"user\": \"user1\", \"engineId\": \"8000000000000001\", \"usmHMACMD5Auth\": \"authkey1\", \"usmDESPriv\": \"privkey1\" }, { \"user\": \"user2\", \"engineId\": \"8000000000000002\", \"usmHMACSHAAuth\": \"authkey2\", \"usmAesCfb128\": \"privkey2\" }, { \"user\": \"user3\", \"engineId\": \"8000000000000003\", \"usmHMACSHAAuth\": \"authkey3\", \"usmAesCfb256\": \"privkey3\" } ] } }")
- post_data_enclosed=json.loads("[{\"epoch_serno\": 15488041460000, \"uuid\": \"bd3a87a0-241c-11e9-ac41-0242ac11000f\", \"agent address\": \"127.0.0.1\", \"agent name\": \"localhost\", \"cambria.partition\": \"localhost\", \"community\": \"\", \"community len\": 0, \"protocol version\": \"v2c\", \"time received\": 1548804146, \"trap category\": \"com.att.dcae.dmaap.FTL.24256-DCAE-COLLECTOR-UCSNMP-v1\", \"notify OID\": \".1.3.6.1.4.1.74.2.46.12.1.1\", \"notify OID len\": 12, \"varbinds\": [{\"varbind_oid\": \".1.3.6.1.4.1.74.2.46.12.1.1.1\", \"varbind_type\": \"octet\", \"varbind_value\": \"ucsnmp heartbeat - ignore\"} ,{\"varbind_oid\": \".1.3.6.1.4.1.74.2.46.12.1.1.2\", \"varbind_type\": \"octet\", \"varbind_value\": \"Tue Jan 29 23:22:26 2019\"}]}, {\"epoch_serno\": 15488041560000, \"uuid\": \"c338a4b6-241c-11e9-ac41-0242ac11000f\", \"agent address\": \"127.0.0.1\", \"agent name\": \"localhost\", \"cambria.partition\": \"localhost\", \"community\": \"\", \"community len\": 0, \"protocol version\": \"v2c\", \"time received\": 1548804156, \"trap category\": \"com.att.dcae.dmaap.FTL.24256-DCAE-COLLECTOR-UCSNMP-v1\", \"notify OID\": \".1.3.6.1.4.1.74.2.46.12.1.1\", \"notify OID len\": 12, \"varbinds\": [{\"varbind_oid\": \".1.3.6.1.4.1.74.2.46.12.1.1.1\", \"varbind_type\": \"octet\", \"varbind_value\": \"ucsnmp heartbeat - ignore\"} ,{\"varbind_oid\": \".1.3.6.1.4.1.74.2.46.12.1.1.2\", \"varbind_type\": \"octet\", \"varbind_value\": \"Tue Jan 29 23:22:36 2019\"}]}]")
-
-
-def test_open_eelf_error_file():
- """
- Test bad error file location
- """
-
- # open eelf error logs
- tds.c_config['files.eelf_error']="/bad_dir/error.log"
-
- # try to open file in non-existent dir
- with pytest.raises(SystemExit) as pytest_wrapped_exception:
- result = trapd_io.open_eelf_logs()
- assert pytest_wrapped_exception.type == SystemExit
-
-def test_open_eelf_debug_file():
- """
- Test bad debug file location
- """
-
- # open eelf debug logs
- tds.c_config['files.eelf_debug']="/bad_dir/debug.log"
-
- # try to open file in non-existent dir
- with pytest.raises(SystemExit) as pytest_wrapped_exception:
- result = trapd_io.open_eelf_logs()
- assert pytest_wrapped_exception.type == SystemExit
-def test_open_eelf_audit_file():
- """
- Test bad audit file location
- """
-
- # open eelf debug logs
- tds.c_config['files.eelf_audit']="/bad_dir/audit.log"
-
- # try to open file in non-existent dir
- with pytest.raises(SystemExit) as pytest_wrapped_exception:
- result = trapd_io.open_eelf_logs()
- assert pytest_wrapped_exception.type == SystemExit
-
-def test_open_eelf_metrics_file():
- """
- Test bad metrics file location
- """
-
- # open eelf debug logs
- tds.c_config['files.eelf_metrics']="/bad_dir/metrics.log"
-
- # try to open file in non-existent dir
- with pytest.raises(SystemExit) as pytest_wrapped_exception:
- result = trapd_io.open_eelf_logs()
- assert pytest_wrapped_exception.type == SystemExit
-
-def test_roll_all_logs():
- """
- Test roll of logs when not open
- """
- # try to roll logs when not open
- with pytest.raises(SystemExit) as pytest_wrapped_exception:
- result = trapd_io.roll_all_logs()
- assert pytest_wrapped_exception.type == SystemExit
-
-def test_roll_file():
- """
- Test roll of individual file when not present
- """
-
- # try to roll logs when not open
- assert trapd_io.roll_file("/file/not/present") == False
-
-def test_open_file_exists():
- """
- Test file open in directory present
- """
-
- # create copy of snmptrapd.json for pytest
- test_file = "/tmp/snmptrap_pytest"
-
- # try to roll logs when not open
- result = trapd_io.open_file(test_file)
- assert str(result).startswith("<_io.TextIOWrapper name=") == True
-
-
-def test_open_file_exists_does_not_exist():
- """
- Test file open in directory present
- """
-
- # create copy of snmptrapd.json for pytest
- test_file = "/tmp/no_such_dir/snmptrap_pytest"
-
- # try to open file when dir not present
- with pytest.raises(SystemExit) as pytest_wrapped_exception:
+ def test_roll_all_files_notopen(self):
+ """
+ Test rolling files that aren't open
+ """
+
+ # try to roll logs when not open
+ with pytest.raises(SystemExit) as pytest_wrapped_exception:
+ result = trapd_io.roll_all_logs()
+ assert pytest_wrapped_exception.type == SystemExit
+
+ def test_open_eelf_error_file(self):
+ """
+ Test bad error file location
+ """
+
+ # open eelf error logs
+ tds.c_config['files.eelf_error']="/bad_dir/error.log"
+
+ # try to open file in non-existent dir
+ with pytest.raises(SystemExit) as pytest_wrapped_exception:
+ result = trapd_io.open_eelf_logs()
+ assert pytest_wrapped_exception.type == SystemExit
+
+ def test_open_eelf_debug_file(self):
+ """
+ Test bad debug file location
+ """
+
+ # open eelf debug logs
+ tds.c_config['files.eelf_debug']="/bad_dir/debug.log"
+
+ # try to open file in non-existent dir
+ with pytest.raises(SystemExit) as pytest_wrapped_exception:
+ result = trapd_io.open_eelf_logs()
+ assert pytest_wrapped_exception.type == SystemExit
+
+ def test_open_eelf_audit_file(self):
+ """
+ Test bad audit file location
+ """
+
+ # open eelf debug logs
+ tds.c_config['files.eelf_audit']="/bad_dir/audit.log"
+
+ # try to open file in non-existent dir
+ with pytest.raises(SystemExit) as pytest_wrapped_exception:
+ result = trapd_io.open_eelf_logs()
+ assert pytest_wrapped_exception.type == SystemExit
+
+ def test_open_eelf_metrics_file(self):
+ """
+ Test bad metrics file location
+ """
+
+ # open eelf debug logs
+ tds.c_config['files.eelf_metrics']="/bad_dir/metrics.log"
+
+ # try to open file in non-existent dir
+ with pytest.raises(SystemExit) as pytest_wrapped_exception:
+ result = trapd_io.open_eelf_logs()
+ assert pytest_wrapped_exception.type == SystemExit
+
+ def test_roll_all_logs(self):
+ """
+ Test roll of logs when not open
+ """
+
+ # try to roll logs when not open
+ with pytest.raises(SystemExit) as pytest_wrapped_exception:
+ result = trapd_io.roll_all_logs()
+ assert pytest_wrapped_exception.type == SystemExit
+
+ def test_roll_file(self):
+ """
+ Test roll of individual file when not present
+ """
+
+ # try to roll logs when not open
+ result = trapd_io.roll_file("/file/not/present")
+ self.assertEqual(result, False)
+
+ def test_roll_file_no_write_perms(self):
+ """
+ try to roll logs when not enough perms
+ """
+
+ no_perms_dir="/tmp/opt/app/snmptrap/no_perms"
+ no_perms_file="test.dat"
+ no_perms_fp= no_perms_dir + "/" + no_perms_file
+
+ # required directory tree
+ try:
+ Path(no_perms_dir).mkdir(parents=True, exist_ok=True)
+ os.chmod(no_perms_dir,0o777)
+ except Exception as e:
+ print("Error while running %s : %s" % (os.path.basename(__file__), str(e.strerror)))
+ sys.exit(1)
+
+ # create empty file
+ open(no_perms_fp,'a').close()
+ os.chmod(no_perms_dir,0o444)
+
+ result = trapd_io.roll_file(no_perms_fp)
+ self.assertEqual(result, False)
+
+ # try to roll file in dir with no write perms
+ with pytest.raises(SystemExit) as pytest_wrapped_exception:
+ result = trapd_io.open_file(no_perms_fp)
+ assert pytest_wrapped_exception.type == SystemExit
+
+
+ def test_open_file_exists(self):
+ """
+ Test file open in directory present
+ """
+
+ # create copy of snmptrapd.json for pytest
+ test_file = "/tmp/snmptrap_pytest"
+
+ # try to roll logs when not open
result = trapd_io.open_file(test_file)
- assert pytest_wrapped_exception.type == SystemExit
-
-def test_close_file_exists():
- """
- Test closing a file that's present
- """
-
- # create copy of snmptrapd.json for pytest
- test_file_name = "/tmp/snmptrap_pytest"
- test_file = trapd_io.open_file(test_file_name)
-
- # close active file
- assert trapd_io.close_file(test_file, test_file_name) == True
-
-def test_close_file_does_not_exists():
- """
- Test closing non-existent file
- """
-
- # try to roll logs when not open
- assert trapd_io.close_file(None, None) == False
+ compare = str(result).startswith("<_io.TextIOWrapper name=")
+ self.assertEqual(compare, True)
+
+ def test_open_file_exists_does_not_exist(self):
+ """
+ Test file open in directory present
+ """
+
+ # create copy of snmptrapd.json for pytest
+ test_file = "/tmp/no_such_dir/snmptrap_pytest"
+
+ # try to open file when dir not present
+ with pytest.raises(SystemExit) as pytest_wrapped_exception:
+ result = trapd_io.open_file(test_file)
+ assert pytest_wrapped_exception.type == SystemExit
+
+ def test_close_file_exists(self):
+ """
+ Test closing a file that's present
+ """
+
+ # create copy of snmptrapd.json for pytest
+ test_file_name = "/tmp/snmptrap_pytest"
+ test_file = trapd_io.open_file(test_file_name)
+
+ # close active file
+ result = trapd_io.close_file(test_file, test_file_name)
+ self.assertEqual(result, True)
+
+ def test_close_file_does_not_exists(self):
+ """
+ Test closing non-existent file
+ """
+
+ # try to roll logs when not open
+ result = trapd_io.close_file(None, None)
+ self.assertEqual(result, False)
+
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/tests/test_trapd_runtime_pid.py b/tests/test_trapd_runtime_pid.py
index b9010e1..e22b6cb 100644
--- a/tests/test_trapd_runtime_pid.py
+++ b/tests/test_trapd_runtime_pid.py
@@ -1,3 +1,19 @@
+# ============LICENSE_START=======================================================
+# Copyright (c) 2019-2020 AT&T Intellectual Property. All rights reserved.
+# ================================================================================
+# 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.
+# ============LICENSE_END=========================================================
+
import pytest
import unittest
import trapd_runtime_pid
diff --git a/tests/test_trapd_settings.py b/tests/test_trapd_settings.py
index 17b20a8..9d5cee2 100644
--- a/tests/test_trapd_settings.py
+++ b/tests/test_trapd_settings.py
@@ -1,3 +1,19 @@
+# ============LICENSE_START=======================================================
+# Copyright (c) 2019-2020 AT&T Intellectual Property. All rights reserved.
+# ================================================================================
+# 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.
+# ============LICENSE_END=========================================================
+
import pytest
import unittest
import trapd_exit
diff --git a/tests/test_trapd_snmpv3.py b/tests/test_trapd_snmpv3.py
index 2a0ef12..478f479 100644
--- a/tests/test_trapd_snmpv3.py
+++ b/tests/test_trapd_snmpv3.py
@@ -1,3 +1,19 @@
+# ============LICENSE_START=======================================================
+# Copyright (c) 2019-2020 AT&T Intellectual Property. All rights reserved.
+# ================================================================================
+# 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.
+# ============LICENSE_END=========================================================
+
import pytest
import json
import unittest
diff --git a/tests/test_trapd_stormwatch.py b/tests/test_trapd_stormwatch.py
new file mode 100644
index 0000000..236157e
--- /dev/null
+++ b/tests/test_trapd_stormwatch.py
@@ -0,0 +1,54 @@
+# ============LICENSE_START=======================================================
+# Copyright (c) 2020 AT&T Intellectual Property. All rights reserved.
+# ================================================================================
+# 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.
+# ============LICENSE_END=========================================================
+
+import pytest
+import unittest
+import trapd_exit
+
+import trapd_stormwatch as sw
+import trapd_stormwatch_settings as sws
+import trapd_stats_settings as stats
+
+class test_cleanup_and_exit(unittest.TestCase):
+ """
+ Test for presense of required vars
+ """
+
+
+ def test_increment_existing_counter(self):
+ """
+ Test increment counter
+ """
+ sw.sw_init()
+ stats.init()
+
+ oid=".1.2.3.4.5.6"
+ sws.sw_config_oid_dict[oid] = True
+ sws.sw_config_low_water_in_interval_dict[oid] = 1
+ sws.sw_config_high_water_in_interval_dict[oid] = 10
+
+ try:
+ sw.stats_increment_counters("192.168.1.1", ".1.2.3.4.5.6")
+ result = True
+ except:
+ result = False
+
+ self.assertEqual(result, True)
+
+
+if __name__ == '__main__':
+ # sws.init()
+ unittest.main()
diff --git a/tests/test_trapd_stormwatch_settings.py b/tests/test_trapd_stormwatch_settings.py
new file mode 100644
index 0000000..ba8ddb0
--- /dev/null
+++ b/tests/test_trapd_stormwatch_settings.py
@@ -0,0 +1,158 @@
+# ============LICENSE_START=======================================================
+# Copyright (c) 2020 AT&T Intellectual Property. All rights reserved.
+# ================================================================================
+# 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.
+# ============LICENSE_END=========================================================
+
+import pytest
+import unittest
+import trapd_exit
+
+pid_file="/tmp/test_pid_file"
+pid_file_dne="/tmp/test_pid_file_NOT"
+
+import trapd_stormwatch_settings as sws
+
+class test_cleanup_and_exit(unittest.TestCase):
+ """
+ Test for presense of required vars
+ """
+
+
+ def test_nonexistent_dict(self):
+ """
+ Test nosuch var
+ """
+ sws.init()
+ try:
+ sws.no_such_var
+ result = True
+ except:
+ result = False
+
+ self.assertEqual(result, False)
+
+ def test_storm_counter_dict(self):
+ """
+ Test storm_counter_dict
+ """
+ sws.init()
+ try:
+ sws.sw_storm_counter_dict
+ result = True
+ except:
+ result = False
+
+ self.assertEqual(result, True)
+
+ def test_storm_active_dict(self):
+ """
+ Test storm_active_dict
+ """
+
+ sws.init()
+ try:
+ sws.sw_storm_active_dict
+ result = True
+ except:
+ result = False
+
+ self.assertEqual(result, True)
+
+ def test_sw_config_oid_dict(self):
+ """
+ Test sw_config_oid_dict
+ """
+
+ sws.init()
+ try:
+ sws.sw_config_oid_dict
+ result = True
+ except:
+ result = False
+
+ self.assertEqual(result, True)
+
+ def test_sw_config_low_water_in_interval_dict(self):
+ """
+ Test low_water
+ """
+
+ sws.init()
+ try:
+ sws.sw_config_low_water_in_interval_dict
+ result = True
+ except:
+ result = False
+
+ self.assertEqual(result, True)
+
+ def test_sw_config_high_water_in_interval_dict(self):
+ """
+ Test high water dict
+ """
+
+ sws.init()
+ try:
+ sws.sw_config_high_water_in_interval_dict
+ result = True
+ except:
+ result = False
+
+ self.assertEqual(result, True)
+
+ def test_sw_config_category(self):
+ """
+ Test category
+ """
+
+ sws.init()
+ try:
+ sws.sw_config_category
+ result = True
+ except:
+ result = False
+
+ self.assertEqual(result, True)
+
+ def test_sw_interval_in_seconds(self):
+ """
+ Test sw_interval
+ """
+
+ sws.init()
+ try:
+ str(sws.sw_interval_in_seconds).isnumeric()
+ result = True
+ except:
+ result = False
+
+ self.assertEqual(result, True)
+
+ def test_sw_last_stormwatch_dict_analysis(self):
+ """
+ Test last_stormwatch_dict_analysis
+ """
+
+ sws.init()
+ try:
+ str(sws.sw_last_stormwatch_dict_analysis).isnumeric()
+ result = True
+ except:
+ result = False
+
+ self.assertEqual(result, True)
+
+if __name__ == '__main__':
+ # sws.init()
+ unittest.main()
diff --git a/tests/test_trapd_vb_types.py b/tests/test_trapd_vb_types.py
index 67c9233..1cf75ab 100644
--- a/tests/test_trapd_vb_types.py
+++ b/tests/test_trapd_vb_types.py
@@ -1,3 +1,19 @@
+# ============LICENSE_START=======================================================
+# Copyright (c) 2019-2020 AT&T Intellectual Property. All rights reserved.
+# ================================================================================
+# 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.
+# ============LICENSE_END=========================================================
+
import pytest
import json
import unittest
@@ -16,25 +32,111 @@ class test_trapd_vb_types(unittest.TestCase):
Test snmpv3 module
"""
- def trapd_vb_type_conversion_valid(self):
+ good_varbind_types = ["Integer", "Unsigned32", "Counter32", "OctetString", "ObjectIdentifier", "TimeTicks", "IpAddress"]
+
+ def trapd_vb_type_conversion_integer(self):
"""
- Test that pysnmp varbind types convert to netsnmp
+ Test that pysnmp varbind types Integer converts
"""
- tds.c_config = json.loads("{ \"snmptrapd\": { \"version\": \"1.4.0\", \"title\": \"ONAP SNMP Trap Receiver\" }, \"protocols\": { \"transport\": \"udp\", \"ipv4_interface\": \"0.0.0.0\", \"ipv4_port\": 6162, \"ipv6_interface\": \"::1\", \"ipv6_port\": 6162 }, \"cache\": { \"dns_cache_ttl_seconds\": 60 }, \"publisher\": { \"http_timeout_milliseconds\": 1500, \"http_retries\": 3, \"http_milliseconds_between_retries\": 750, \"http_primary_publisher\": \"true\", \"http_peer_publisher\": \"unavailable\", \"max_traps_between_publishes\": 10, \"max_milliseconds_between_publishes\": 10000 }, \"streams_publishes\": { \"sec_fault_unsecure\": { \"type\": \"message_router\", \"aaf_password\": null, \"dmaap_info\": { \"location\": \"mtl5\", \"client_id\": null, \"client_role\": null, \"topic_url\": \"http://localhost:3904/events/ONAP-COLLECTOR-SNMPTRAP\" }, \"aaf_username\": null } }, \"files\": { \"runtime_base_dir\": \"/tmp/opt/app/snmptrap\", \"log_dir\": \"logs\", \"data_dir\": \"data\", \"pid_dir\": \"tmp\", \"arriving_traps_log\": \"snmptrapd_arriving_traps.log\", \"snmptrapd_diag\": \"snmptrapd_prog_diag.log\", \"traps_stats_log\": \"snmptrapd_stats.csv\", \"perm_status_file\": \"snmptrapd_status.log\", \"eelf_base_dir\": \"/tmp/opt/app/snmptrap/logs\", \"eelf_error\": \"error.log\", \"eelf_debug\": \"debug.log\", \"eelf_audit\": \"audit.log\", \"eelf_metrics\": \"metrics.log\", \"roll_frequency\": \"day\", \"minimum_severity_to_log\": 2 }, \"trap_config\": { \"sw_interval_in_seconds\": 60, \"notify_oids\": { \".1.3.6.1.4.1.9.0.1\": { \"sw_high_water_in_interval\": 102, \"sw_low_water_in_interval\": 7, \"category\": \"logonly\" }, \".1.3.6.1.4.1.9.0.2\": { \"sw_high_water_in_interval\": 101, \"sw_low_water_in_interval\": 7, \"category\": \"logonly\" }, \".1.3.6.1.4.1.9.0.3\": { \"sw_high_water_in_interval\": 102, \"sw_low_water_in_interval\": 7, \"category\": \"logonly\" }, \".1.3.6.1.4.1.9.0.4\": { \"sw_high_water_in_interval\": 10, \"sw_low_water_in_interval\": 3, \"category\": \"logonly\" } } } }")
+ result = trapd_vb_types.pysnmp_to_netsnmp_varbind_convert("Integer32")
+ self.assertEqual(result, "integer")
+
+ def trapd_vb_type_conversion_integer(self):
+ """
+ Test that pysnmp varbind types Integer converts
+ """
- good_varbind_types = ["Integer", "Unsigned32", "Counter32", "OctetString", "ObjectIdentifier", "TimeTicks", "IpAddress"]
result = trapd_vb_types.pysnmp_to_netsnmp_varbind_convert("Integer")
self.assertEqual(result, "integer")
- def trapd_vb_type_conversion_invalid(self):
+ def trapd_vb_type_conversion_integer(self):
+ """
+ Test that pysnmp varbind types Integer converts
+ """
+
+ result = trapd_vb_types.pysnmp_to_netsnmp_varbind_convert("Gauge32")
+ self.assertEqual(result, "unsigned")
+
+ def trapd_vb_type_conversion_integer(self):
+ """
+ Test that pysnmp varbind types Integer converts
+ """
+
+ result = trapd_vb_types.pysnmp_to_netsnmp_varbind_convert("Counter32")
+ self.assertEqual(result, "counter32")
+
+ def trapd_vb_type_conversion_integer(self):
+ """
+ Test that pysnmp varbind types convert accurately
+ """
+
+ result = trapd_vb_types.pysnmp_to_netsnmp_varbind_convert("OctetString")
+ self.assertEqual(result, "octet")
+
+ def trapd_vb_type_conversion_integer(self):
+ """
+ Test that pysnmp varbind types convert accurately
+ """
+
+ result = trapd_vb_types.pysnmp_to_netsnmp_varbind_convert("py_type_5")
+ self.assertEqual(result, "hex")
+
+ def trapd_vb_type_conversion_integer(self):
"""
- Test that pysnmp varbind types convert to netsnmp
+ Test that pysnmp varbind types convert accurately
"""
- tds.c_config = json.loads("{ \"snmptrapd\": { \"version\": \"1.4.0\", \"title\": \"ONAP SNMP Trap Receiver\" }, \"protocols\": { \"transport\": \"udp\", \"ipv4_interface\": \"0.0.0.0\", \"ipv4_port\": 6162, \"ipv6_interface\": \"::1\", \"ipv6_port\": 6162 }, \"cache\": { \"dns_cache_ttl_seconds\": 60 }, \"publisher\": { \"http_timeout_milliseconds\": 1500, \"http_retries\": 3, \"http_milliseconds_between_retries\": 750, \"http_primary_publisher\": \"true\", \"http_peer_publisher\": \"unavailable\", \"max_traps_between_publishes\": 10, \"max_milliseconds_between_publishes\": 10000 }, \"streams_publishes\": { \"sec_fault_unsecure\": { \"type\": \"message_router\", \"aaf_password\": null, \"dmaap_info\": { \"location\": \"mtl5\", \"client_id\": null, \"client_role\": null, \"topic_url\": \"http://localhost:3904/events/ONAP-COLLECTOR-SNMPTRAP\" }, \"aaf_username\": null } }, \"files\": { \"runtime_base_dir\": \"/tmp/opt/app/snmptrap\", \"log_dir\": \"logs\", \"data_dir\": \"data\", \"pid_dir\": \"tmp\", \"arriving_traps_log\": \"snmptrapd_arriving_traps.log\", \"snmptrapd_diag\": \"snmptrapd_prog_diag.log\", \"traps_stats_log\": \"snmptrapd_stats.csv\", \"perm_status_file\": \"snmptrapd_status.log\", \"eelf_base_dir\": \"/tmp/opt/app/snmptrap/logs\", \"eelf_error\": \"error.log\", \"eelf_debug\": \"debug.log\", \"eelf_audit\": \"audit.log\", \"eelf_metrics\": \"metrics.log\", \"roll_frequency\": \"day\", \"minimum_severity_to_log\": 2 }, \"trap_config\": { \"sw_interval_in_seconds\": 60, \"notify_oids\": { \".1.3.6.1.4.1.9.0.1\": { \"sw_high_water_in_interval\": 102, \"sw_low_water_in_interval\": 7, \"category\": \"logonly\" }, \".1.3.6.1.4.1.9.0.2\": { \"sw_high_water_in_interval\": 101, \"sw_low_water_in_interval\": 7, \"category\": \"logonly\" }, \".1.3.6.1.4.1.9.0.3\": { \"sw_high_water_in_interval\": 102, \"sw_low_water_in_interval\": 7, \"category\": \"logonly\" }, \".1.3.6.1.4.1.9.0.4\": { \"sw_high_water_in_interval\": 10, \"sw_low_water_in_interval\": 3, \"category\": \"logonly\" } } } }")
+ result = trapd_vb_types.pysnmp_to_netsnmp_varbind_convert("py_type_6")
+ self.assertEqual(result, "decimal")
+
+ def trapd_vb_type_conversion_integer(self):
+ """
+ Test that pysnmp varbind types convert accurately
+ """
+
+ result = trapd_vb_types.pysnmp_to_netsnmp_varbind_convert("Null")
+ self.assertEqual(result, "null")
+
+ def trapd_vb_type_conversion_integer(self):
+ """
+ Test that pysnmp varbind types convert accurately
+ """
+
+ result = trapd_vb_types.pysnmp_to_netsnmp_varbind_convert("ObjectIdentifier")
+ self.assertEqual(result, "oid")
+
+ def trapd_vb_type_conversion_integer(self):
+ """
+ Test that pysnmp varbind types convert accurately
+ """
+
+ result = trapd_vb_types.pysnmp_to_netsnmp_varbind_convert("TimeTicks")
+ self.assertEqual(result, "timeticks")
+
+ def trapd_vb_type_conversion_integer(self):
+ """
+ Test that pysnmp varbind types convert accurately
+ """
+
+ result = trapd_vb_types.pysnmp_to_netsnmp_varbind_convert("IpAddress")
+ self.assertEqual(result, "ipaddress")
+
+ def trapd_vb_type_conversion_integer(self):
+ """
+ Test that pysnmp varbind types convert accurately
+ """
+
+ result = trapd_vb_types.pysnmp_to_netsnmp_varbind_convert("Bits")
+ self.assertEqual(result, "bits")
+
+ def trapd_vb_type_conversion_invalid(self):
+ """
+ Test that pysnmp varbind types convert accurately
+ """
result = trapd_vb_types.pysnmp_to_netsnmp_varbind_convert("noSuchVarbindType")
+ # should return default of octet if not defined
self.assertEqual(result, "octet")
diff --git a/version.properties b/version.properties
index f352992..c8a67b0 100644
--- a/version.properties
+++ b/version.properties
@@ -1,6 +1,6 @@
-major=1
-minor=4
-patch=1
+major=2
+minor=0
+patch=3
base_version=${major}.${minor}.${patch}
release_version=${base_version}
snapshot_version=${base_version}-SNAPSHOT