summaryrefslogtreecommitdiffstats
path: root/bin
diff options
context:
space:
mode:
authorLadue, David (dl3158) <dl3158@att.com>2018-03-14 14:01:36 -0400
committerLadue, David (dl3158) <dl3158@att.com>2018-03-18 02:42:23 -0400
commitabb283c5a3b72eb2d91e6adb4eda68184f040f35 (patch)
tree4765c8d6cb54a2ddb2052c0955425604bc265230 /bin
parenta6f91efda578965710630154b173cf1ee9b64727 (diff)
copyright and controller ENV changes
Change-Id: Ic12aa439a03f19c7a06a536368a8d2a1f26855aa Issue-ID: DCAEGEN2-271 Signed-off-by: Ladue, David (dl3158) <dl3158@att.com>
Diffstat (limited to 'bin')
-rw-r--r--bin/mod/trapd_exit.py63
-rw-r--r--bin/mod/trapd_file_utils.py220
-rw-r--r--bin/mod/trapd_get_cbs_config.py116
-rw-r--r--bin/mod/trapd_http_session.py61
-rw-r--r--bin/mod/trapd_logging.py199
-rw-r--r--bin/mod/trapd_runtime_pid.py94
-rw-r--r--bin/mod/trapd_settings.py168
-rw-r--r--bin/snmptrapd.py766
-rwxr-xr-xbin/snmptrapd.sh52
9 files changed, 1739 insertions, 0 deletions
diff --git a/bin/mod/trapd_exit.py b/bin/mod/trapd_exit.py
new file mode 100644
index 0000000..a7ffc8a
--- /dev/null
+++ b/bin/mod/trapd_exit.py
@@ -0,0 +1,63 @@
+# ============LICENSE_START=======================================================
+# org.onap.dcae
+# ================================================================================
+# Copyright (c) 2017-2018 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.
+#
+"""
+trapc_exit_snmptrapd is responsible for removing any existing runtime PID
+file, and exiting with the provided (param 1) exit code
+"""
+
+__docformat__ = 'restructuredtext'
+
+import sys
+import os
+import string
+from trapd_runtime_pid import save_pid, rm_pid
+
+prog_name = os.path.basename(__file__)
+
+
+# # # # # # # # # # # # #
+# fx: cleanup_and_exit
+# - remove pid file
+# - exit with supplied return code
+# # # # # # # # # # # # #
+def cleanup_and_exit(_loc_exit_code, _pid_file_name):
+ """
+ Remove existing PID file, and exit with provided exit code
+ :Parameters:
+ _loc_exit_code
+ value to return to calling shell upon exit
+ _pid_file_name
+ name of file that contains current process ID (for
+ removal)
+ :Exceptions:
+ none
+ :Keywords:
+ runtime PID exit
+ :Variables:
+ _num_params
+ number of parameters passed to module
+ """
+
+ _num_params = len(locals())
+
+ if _num_params == 2:
+ rc = rm_pid(_pid_file_name)
+ sys.exit(_loc_exit_code)
diff --git a/bin/mod/trapd_file_utils.py b/bin/mod/trapd_file_utils.py
new file mode 100644
index 0000000..2da099b
--- /dev/null
+++ b/bin/mod/trapd_file_utils.py
@@ -0,0 +1,220 @@
+# ============LICENSE_START=======================================================)
+# org.onap.dcae
+# ================================================================================
+# Copyright (c) 2018 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.
+#
+"""
+"""
+
+__docformat__ = 'restructuredtext'
+
+# basics
+import datetime
+import errno
+import inspect
+import json
+import logging
+import logging.handlers
+import os
+import sys
+import string
+import time
+import traceback
+import unicodedata
+
+# dcae_snmptrap
+import trapd_settings as tds
+from trapd_logging import ecomp_logger, stdout_logger
+from trapd_exit import cleanup_and_exit
+
+prog_name = os.path.basename(__file__)
+
+
+# # # # # # # # # # # # # # # # # # #
+# fx: roll_all_logs -> roll all logs to timestamped backup
+# # # # # # # # # # ## # # # # # # #
+
+
+def roll_all_logs():
+ """
+ roll all active logs to timestamped version, open new one
+ based on frequency defined in files.roll_frequency
+ """
+
+ # first roll all the eelf files
+ # NOTE: this will go away when onap logging is standardized/available
+ try:
+ # open various ecomp logs - if any fails, exit
+ for fd in [tds.eelf_error_fd, tds.eelf_debug_fd, tds.eelf_audit_fd, \
+ tds.eelf_metrics_fd, tds.arriving_traps_fd, tds.json_traps_fd]:
+ fd.close()
+
+ roll_file(tds.eelf_error_file_name)
+ roll_file(tds.eelf_debug_file_name)
+ roll_file(tds.eelf_audit_file_name)
+ roll_file(tds.eelf_metrics_file_name)
+
+ except Exception as e:
+ msg = "Error closing logs: " + str(e)
+ stdout_logger(msg)
+ cleanup_and_exit(1, tds.pid_file_name)
+
+ reopened_successfully = open_eelf_logs()
+ if not reopened_successfully:
+ msg = "Error re-opening EELF logs during roll-over to timestamped versions - EXITING"
+ stdout_logger(msg)
+ cleanup_and_exit(1, tds.pid_file_name)
+
+ # json log
+ roll_file(tds.json_traps_filename)
+
+ try:
+ tds.json_traps_fd = open_file(tds.json_traps_filename)
+ except Exception as e:
+ msg = ("Error opening json_log %s : %s" % (json_traps_filename, str(e)))
+ stdout_logger(msg)
+ cleanup_and_exit(1, tds.pid_file_name)
+
+ # arriving trap log
+ roll_file(tds.arriving_traps_filename)
+
+ try:
+ tds.arriving_traps_fd = open_file(tds.arriving_traps_filename)
+ except Exception as e:
+ msg = ("Error opening arriving traps %s : %s" % (arriving_traps_filename, str(e)))
+ stdout_logger(msg)
+ cleanup_and_exit(1, tds.pid_file_name)
+
+
+
+# # # # # # # # # # # # # # # # # # #
+# fx: setup_ecomp_logs -> log in eelf format until standard
+# is released for python via LOG-161
+# # # # # # # # # # ## # # # # # # #
+
+
+def open_eelf_logs():
+ """
+ open various (multiple ???) logs
+ """
+
+ try:
+ # open various ecomp logs - if any fails, exit
+
+ tds.eelf_error_file_name = (tds.c_config['files.eelf_base_dir'] + "/" + tds.c_config['files.eelf_error'])
+ tds.eelf_error_fd = open_file(tds.eelf_error_file_name)
+
+ except Exception as e:
+ msg = "Error opening eelf error log : " + str(e)
+ stdout_logger(msg)
+ cleanup_and_exit(1, tds.pid_file_name)
+
+
+ try:
+ tds.eelf_debug_file_name = (tds.c_config['files.eelf_base_dir'] + "/" + tds.c_config['files.eelf_debug'])
+ tds.eelf_debug_fd = open_file(tds.eelf_debug_file_name)
+
+ except Exception as e:
+ msg = "Error opening eelf debug log : " + str(e)
+ stdout_logger(msg)
+ cleanup_and_exit(1, tds.pid_file_name)
+
+ try:
+ tds.eelf_audit_file_name = (tds.c_config['files.eelf_base_dir'] + "/" + tds.c_config['files.eelf_audit'])
+ tds.eelf_audit_fd = open_file(tds.eelf_audit_file_name)
+ except Exception as e:
+ msg = "Error opening eelf audit log : " + str(e)
+ stdout_logger(msg)
+ cleanup_and_exit(1, tds.pid_file_name)
+
+ try:
+ tds.eelf_metrics_file_name = (tds.c_config['files.eelf_base_dir'] + "/" + tds.c_config['files.eelf_metrics'])
+ tds.eelf_metrics_fd = open_file(tds.eelf_metrics_file_name)
+ except Exception as e:
+ msg = "Error opening eelf metric log : " + str(e)
+ stdout_logger(msg)
+ cleanup_and_exit(1, tds.pid_file_name)
+
+ return True
+
+# # # # # # # # # # # # # # # # # # #
+# fx: roll_log_file -> move provided filename to timestamped version
+# # # # # # # # # # ## # # # # # # #
+
+
+def roll_file(_loc_file_name):
+ """
+ move active file to timestamped archive
+ """
+
+ _file_name_suffix = "%s" % (datetime.datetime.fromtimestamp(time.time()).
+ fromtimestamp(time.time()).
+ strftime('%Y-%m-%dT%H:%M:%S'))
+
+ _loc_file_name_bak = _loc_file_name + '.' + _file_name_suffix
+
+ # roll existing file if present
+ if os.path.isfile(_loc_file_name):
+ try:
+ os.rename(_loc_file_name, _loc_file_name_bak)
+ except:
+ _msg = ("ERROR: Unable to rename %s to %s"
+ % (_loc_file_name,
+ _loc_file_name_bak))
+ ecomp_logger(tds.LOG_TYPE_ERROR, tds.SEV_CRIT, tds.CODE_GENERAL, _msg)
+
+
+# # # # # # # # # # # # #
+# fx: open_log_file
+# # # # # # # # # # # # #
+
+
+def open_file(_loc_file_name):
+ """
+ open _loc_file_name, return file handle
+ """
+
+
+ try:
+ # open append mode just in case so nothing is lost, but should be
+ # non-existent file
+ _loc_fd = open(_loc_file_name, 'a')
+ return _loc_fd
+ except Exception as e:
+ msg = "Error opening " + _loc_file_name + " append mode - " + str(e)
+ stdout_logger(msg)
+ cleanup_and_exit(1, tds.pid_file_name)
+
+
+# # # # # # # # # # # # #
+# fx: close_file
+# # # # # # # # # # # # #
+ """
+ close _loc_file_name, return True with success, False otherwise
+ """
+
+
+def close_file(_loc_fd, _loc_filename):
+
+ try:
+ _loc_fd.close()
+ return True
+ except Exception as e:
+ msg = "Error closing %s : %s - results indeterminate" % (_loc_filename, str(e))
+ ecomp_logger(tds.LOG_TYPE_ERROR, tds.SEV_FATAL, tds.CODE_GENERAL, msg)
+ return False
diff --git a/bin/mod/trapd_get_cbs_config.py b/bin/mod/trapd_get_cbs_config.py
new file mode 100644
index 0000000..775e0b2
--- /dev/null
+++ b/bin/mod/trapd_get_cbs_config.py
@@ -0,0 +1,116 @@
+# ============LICENSE_START=======================================================
+# org.onap.dcae
+# ================================================================================
+# Copyright (c) 2018 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.
+#
+"""
+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
+testing purposes)
+"""
+
+__docformat__ = 'restructuredtext'
+
+import json
+import os
+import sys
+import string
+import time
+import traceback
+import collections
+
+import trapd_settings as tds
+from onap_dcae_cbs_docker_client.client import get_config
+from trapd_exit import cleanup_and_exit
+from trapd_logging import stdout_logger
+
+prog_name = os.path.basename(__file__)
+
+
+# # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
+# function: trapd_get_config_sim
+# # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
+
+
+def get_cbs_config():
+ """
+ Get config values from CBS or JSON file (fallback)
+ :Parameters:
+ none
+ :Exceptions:
+ """
+
+ tds.c_config = {}
+
+ # See if we are in a config binding service (CBS) /controller environment
+ try:
+ tds.c_config = get_config()
+ if tds.c_config == {}:
+ msg = "Unable to fetch CBS config or it is erroneously empty - trying override/simulator config"
+ stdout_logger(msg)
+
+ # if no CBS present, default to JSON config specified via CBS_SIM_JSON env var
+ except Exception as e:
+ msg = "ONAP controller not present, trying json config override via CBS_SIM_JSON env variable"
+ stdout_logger(msg)
+
+ try:
+ _cbs_sim_json_file = os.getenv("CBS_SIM_JSON", "None")
+ except Exception as e:
+ msg = "CBS_SIM_JSON not defined - FATAL ERROR, exiting"
+ stdout_logger(msg)
+ cleanup_and_exit(1, pid_file_name)
+
+ if _cbs_sim_json_file == "None":
+ msg = "CBS_SIM_JSON not defined - FATAL ERROR, exiting"
+ stdout_logger(msg)
+ cleanup_and_exit(1, pid_file_name)
+ 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))
+ 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, tds.pid_file_name)
+
+ # recalc timeout, set default if not present
+ try:
+ tds.timeout_seconds = tds.c_config['publisher.http_timeout_milliseconds'] / 1000.0
+ except:
+ tds.timeout_seconds = 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
+ except:
+ tds.seconds_between_retries = .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']
+ except:
+ tds.minimum_severity_to_log = 3
+
+ try:
+ tds.publisher_retries = tds.c_config['publisher.http_retries']
+ except:
+ tds.publisher_retries = 3
+
+ return True
diff --git a/bin/mod/trapd_http_session.py b/bin/mod/trapd_http_session.py
new file mode 100644
index 0000000..2e0b77e
--- /dev/null
+++ b/bin/mod/trapd_http_session.py
@@ -0,0 +1,61 @@
+# ============LICENSE_START=======================================================
+# org.onap.dcae
+# ================================================================================
+# Copyright (c) 2017-2018 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.
+#
+"""
+trapd_http_session establishes an http session for future use in publishing
+messages to the dmaap cluster.
+"""
+
+__docformat__ = 'restructuredtext'
+
+import os
+import requests
+import traceback
+
+# snmptrapd
+import trapd_settings
+
+prog_name = os.path.basename(__file__)
+
+
+# # # # # # # # # # # # #
+# fx: init_session_obj
+# # # # # # # # # # # # #
+def init_session_obj():
+ """
+ Initializes and returns a http request session object for later use
+ :Parameters:
+ none
+ :Exceptions:
+ session object creation
+ this function will throw an exception if unable to create
+ a new session object
+ :Keywords:
+ http request session
+ :Variables:
+ none
+ """
+
+ try:
+ _loc_session = requests.Session()
+ except Exception as e:
+ return None
+
+ return _loc_session
diff --git a/bin/mod/trapd_logging.py b/bin/mod/trapd_logging.py
new file mode 100644
index 0000000..435f03e
--- /dev/null
+++ b/bin/mod/trapd_logging.py
@@ -0,0 +1,199 @@
+# ============LICENSE_START=======================================================)
+# org.onap.dcae
+# ================================================================================
+# Copyright (c) 2018 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.
+#
+"""
+"""
+
+__docformat__ = 'restructuredtext'
+
+# basics
+import datetime
+import errno
+import inspect
+import json
+import logging
+import logging.handlers
+import os
+import sys
+import string
+import time
+import traceback
+import unicodedata
+
+import trapd_settings as tds
+
+prog_name = os.path.basename(__file__)
+
+
+# # # # # # # # # # # # # # # # # # #
+# fx: ecomp_logger -> log in eelf format until standard
+# is released for python via LOG-161
+# # # # # # # # # # ## # # # # # # #
+
+def ecomp_logger(_log_type, _sev, _error_code, _msg):
+ """
+ Log to ecomp-style logfiles. Logs include:
+
+ Note: this will be updated when https://jira.onap.org/browse/LOG-161
+ is closed/available; until then, we resort to a generic format with
+ valuable info in "extra=" field (?)
+
+ :Parameters:
+ _msg -
+ :Exceptions:
+ none
+ :Keywords:
+ eelf logging
+ :Log Styles:
+
+ :error.log:
+
+ if CommonLogger.verbose: print("using CommonLogger.ErrorFile")
+ self._logger.log(50, '%s|%s|%s|%s|%s|%s|%s|%s|%s|%s' \
+ % (requestID, threadID, serviceName, partnerName, targetEntity, targetServiceName,
+ errorCategory, errorCode, errorDescription, detailMessage))
+
+ error.log example:
+
+ 2018-02-20T07:21:34,007+00:00||MainThread|snmp_log_monitor||||FATAL|900||Tue Feb 20 07:21:11 UTC 2018 CRITICAL: [a0cae74e-160e-11e8-8f9f-0242ac110002] ALL publish attempts failed to DMAPP server: dcae-mrtr-zltcrdm5bdce1.1dff83.rdm5b.tci.att.com, topic: DCAE-COLLECTOR-UCSNMP, 339 trap(s) not published in epoch_serno range: 15191112530000 - 15191112620010
+
+ :debug.log:
+
+ if CommonLogger.verbose: print("using CommonLogger.DebugFile")
+ self._logger.log(50, '%s|%s|%s|%s|%s|%s|%s|%s|%s|%s|%s|%s|%s' \
+ % (requestID, threadID, serverName, serviceName, instanceUUID, upperLogLevel,
+ severity, serverIPAddress, server, IPAddress, className, timer, detailMessage))
+
+ debug.log example:
+
+ none available
+
+ :audit.log:
+
+ if CommonLogger.verbose: print("using CommonLogger.AuditFile")
+ endAuditTime, endAuditMsec = self._getTime()
+ if self._begTime is not None:
+ d = {'begtime': self._begTime, 'begmsecs': self._begMsec, 'endtime': endAuditTime,
+ 'endmsecs': endAuditMsec}
+ else:
+ d = {'begtime': endAuditTime, 'begmsecs': endAuditMsec, 'endtime': endAuditTime,
+ 'endmsecs': endAuditMsec}
+
+ self._logger.log(50, '%s|%s|%s|%s|%s|%s|%s|%s|%s|%s|%s|%s|%s|%s|%s|%s|%s|%s|%s|%s|%s|%s|%s|%s' \
+ % (requestID, serviceInstanceID, threadID, serverName, serviceName, partnerName,
+ statusCode, responseCode, responseDescription, instanceUUID, upperLogLevel,
+ severity, serverIPAddress, timer, server, IPAddress, className, unused,
+ processKey, customField1, customField2, customField3, customField4,
+ detailMessage), extra=d)
+
+
+ :metrics.log:
+
+ self._logger.log(50,'%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' \
+ % (requestID, serviceInstanceID, threadID, serverName, serviceName, partnerName,
+ targetEntity, targetServiceName, statusCode, responseCode, responseDescription,
+ instanceUUID, upperLogLevel, severity, serverIPAddress, timer, server,
+ IPAddress,
+ className, unused, processKey, targetVirtualEntity, customField1, customField2,
+ customField3, customField4, detailMessage), extra=d)
+
+ metrics.log example:
+
+ none available
+
+
+ """
+
+ unused = ""
+
+ # ct = time.time()
+ # lt = time.localtime(ct)
+ # t_hman = time.strftime(DateFmt, lt)
+ # t_ms = (ct - int(ct)) * 1000
+ # 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]
+ calling_fx = inspect.stack()[1][3]
+
+ # FIXME: this entire module is a hack to override concept of prog logging
+ # written across multiple files (???), making diagnostics IMPOSSIBLE!
+ # Hoping to leverage ONAP logging libraries & standards when available
+
+ # catch invalid log type
+ if _log_type < 1 or _log_type > 5:
+ msg = ("INVALID log type: %s " % _log_type )
+ _out_rec = ("%s|%s|%s|%s|%s|%s|%s|%s|%s|%s" \
+ % ((t_out, calling_fx, "snmptrapd", unused, unused, unused, tds.SEV_TYPES[_sev], _error_code, unused, (msg + _msg))))
+ tds.eelf_error_fd.write('%s\n' % str(_out_rec))
+ return False
+
+ 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|%s' \
+ % ((t_out, calling_fx, "snmptrapd", unused, unused, unused, tds.SEV_TYPES[_sev], _error_code, unused, _msg)))
+ tds.eelf_error_fd.write('%s\n' % 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|%s' \
+ % ((t_out, calling_fx, "snmptrapd", unused, unused, unused, tds.SEV_TYPES[_sev], _error_code, unused, _msg)))
+ tds.eelf_audit_fd.write('%s\n' % str(_out_rec))
+ elif _log_type == tds.LOG_TYPE_METRICS:
+ # log message in METRICS format
+ _out_rec = ('%s|%s|%s|%s|%s|%s|%s|%s|%s|%s' \
+ % ((t_out, calling_fx, "snmptrapd", unused, unused, unused, tds.SEV_TYPES[_sev], _error_code, unused, _msg)))
+ tds.eelf_metrics_fd.write('%s\n' % str(_out_rec))
+
+ # DEBUG *AND* others - there *MUST BE* a single time-sequenced log for diagnostics!
+ # FIXME: too much I/O !!!
+ # always write to debug; we need ONE logfile that has time-sequence full view !!!
+ # if (_log_type == tds.LOG_TYPE_DEBUG and _sev >= tds.current_min_sev_log_level) or (_log_type != tds.LOG_TYPE_DEBUG):
+
+ # log message in DEBUG format
+ _out_rec = ("%s|%s|%s|%s|%s|%s|%s|%s|%s|%s" \
+ % ((t_out, calling_fx, "snmptrapd", unused, unused, unused, tds.SEV_TYPES[_sev], _error_code, unused, _msg)))
+ tds.eelf_debug_fd.write('%s\n' % str(_out_rec))
+
+ return True
+
+# # # # # # # # # # # # #
+# fx: stdout_logger
+# # # # # # # # # # # # #
+
+
+def stdout_logger(_msg):
+ """
+ Log info/errors to stdout. This is done:
+ - for critical runtime issues
+
+ :Parameters:
+ _msg
+ message to print
+ :Exceptions:
+ none
+ :Keywords:
+ log stdout
+ :Variables:
+ """
+
+ t_out = datetime.datetime.utcnow().strftime("%Y-%m-%dT%H:%M:%S,%f")[:-3]
+ # calling_fx = inspect.stack()[1][3]
+
+ print('%s %s' % ( t_out, _msg))
diff --git a/bin/mod/trapd_runtime_pid.py b/bin/mod/trapd_runtime_pid.py
new file mode 100644
index 0000000..c6ef76e
--- /dev/null
+++ b/bin/mod/trapd_runtime_pid.py
@@ -0,0 +1,94 @@
+# ============LICENSE_START=======================================================
+# org.onap.dcae
+# ================================================================================
+# Copyright (c) 2017-2018 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.
+#
+"""
+trapd_runtime_pid maintains a 'PID file' (file that contains the
+PID of currently running trap receiver)
+"""
+
+__docformat__ = 'restructuredtext'
+
+import logging
+import os
+import string
+import time
+import traceback
+
+prog_name = os.path.basename(__file__)
+
+
+# # # # # # # # # # # # #
+# fx: save_pid - save PID of running process
+# # # # # # # # # # # # #
+def save_pid(_pid_file_name):
+ """
+ Save the current process ID in a file for external
+ access.
+ :Parameters:
+ none
+ :Exceptions:
+ file open
+ this function will catch exception of unable to
+ open/create _pid_file_name
+ :Keywords:
+ pid /var/run
+ """
+
+ try:
+ pid_fd = open(_pid_file_name, 'w')
+ pid_fd.write('%d' % os.getpid())
+ pid_fd.close()
+ except IOError:
+ print("IOError saving PID file %s :" % _pid_file_name)
+ return False
+ # except:
+ # print("Error saving PID file %s :" % _pid_file_name)
+ # return False
+ else:
+ # print("Runtime PID file: %s" % _pid_file_name)
+ return True
+
+
+# # # # # # # # # # # # #
+# fx: rm_pid - remove PID of running process
+# # # # # # # # # # # # #
+def rm_pid(_pid_file_name):
+ """
+ Remove the current process ID file before exiting.
+ :Parameters:
+ none
+ :Exceptions:
+ file open
+ this function will catch exception of unable to find or remove
+ _pid_file_name
+ :Keywords:
+ pid /var/run
+ """
+
+ try:
+ if os.path.isfile(_pid_file_name):
+ os.remove(_pid_file_name)
+ return True
+ else:
+ return False
+
+ except IOError:
+ print("Error removing Runtime PID file: %s" % _pid_file_name)
+ return False
diff --git a/bin/mod/trapd_settings.py b/bin/mod/trapd_settings.py
new file mode 100644
index 0000000..735b68c
--- /dev/null
+++ b/bin/mod/trapd_settings.py
@@ -0,0 +1,168 @@
+# ============LICENSE_START=======================================================)
+# org.onap.dcae
+# ================================================================================
+# Copyright (c) 2018 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.
+#
+"""
+"""
+
+__docformat__ = 'restructuredtext'
+
+def init():
+
+ # <CONSUL config cache>
+ # consul config or simulated via json file
+ global c_config
+ c_config = None
+ # </CONSUL config cache>
+
+ # <DNS cache>
+ #
+ # dns_cache_ip_to_name
+ # key [ip address] -> fqdn
+ # dns_cache_ip_expires
+ # key [ip address] -> epoch time this entry expires and must be reloaded
+ global dns_cache_ip_to_name
+ dns_cache_ip_to_name = {}
+ global dns_cache_ip_expires
+ dns_cache_ip_expires = {}
+ # </DNS cache>
+
+ # <EELF logs>
+ global eelf_error_file_name
+ eelf_error_file_name = ""
+ global eelf_error_fd
+ eelf_error_fd = None
+
+ global eelf_debug_file_name
+ eelf_debug_file_name = ""
+ global eelf_debug_fd
+ eelf_debug_fd = None
+
+ global eelf_audit_file_name
+ eelf_audit_file_name = ""
+ global eelf_audit_fd
+ eelf_audit_fd = None
+
+ global eelf_metrics_file_name
+ eelf_metrics_file_name = ""
+ global eelf_metrics_fd
+ eelf_metrics_fd = None
+
+ global last_minute
+ last_minute = 0
+ global last_hour
+ last_hour = 0
+ global last_day
+ last_day = 0
+ # </EELF logs>
+
+ # <trap dictionary and corresponding strings for publish
+ global first_trap
+ first_trap = True
+ global first_varbind
+ first_varbind = True
+ global trap_dict
+ trap_dict = {}
+ global all_traps_str
+ all_traps_str = ""
+ global all_vb_json_str
+ all_vb_json_str = ""
+ global trap_uuids_in_buffer
+ trap_uuids_in_buffer = ""
+ # </trap and varbind dictionaries>
+
+ # <publish timers and counters>
+ global traps_in_minute
+ traps_in_minute = 0
+ global last_epoch_second
+ last_epoch_second = 0
+ global traps_since_last_publish
+ traps_since_last_publish = 0
+ global last_pub_time
+ last_pub_time = 0
+ global milliseconds_since_last_publish
+ milliseconds_since_last_publish = 0
+ global timeout_seconds
+ timeout_seconds = 1.5
+ global seconds_between_retries
+ seconds_between_retries = 2
+ global publisher_retries
+ publisher_retries = 2
+ # </publish timers and counters>
+
+ # <publish http request session (persistent as much as possible)>
+ global http_requ_session
+ http_requ_session = None
+ # </publish http request session>
+
+ # <json log of traps published>
+ global json_traps_filename
+ json_log_filename = ""
+ global json_traps_fd
+ json_fd = None
+ # </json log of traps published>
+
+ # <log of arriving traps >
+ global arriving_traps_filename
+ arriving_traps_filename = ""
+ global arriving_traps_fd
+ arriving_traps_fd = None
+ # <log of arriving traps >
+
+ # <runtime PID>
+ global pid_file_name
+ pid_file_name = ""
+
+ # <logging types and severities>
+ global LOG_TYPES
+ global LOG_TYPE_NONE
+ global LOG_TYPE_ERROR
+ global LOG_TYPE_DEBUG
+ global LOG_TYPE_AUDIT
+ global LOG_TYPE_METRICS
+ LOG_TYPES = ["none", "ERROR", "DEBUG", "AUDIT", "METRICS"]
+ LOG_TYPE_NONE = 0
+ LOG_TYPE_ERROR = 1
+ LOG_TYPE_DEBUG = 2
+ LOG_TYPE_AUDIT = 3
+ LOG_TYPE_METRICS = 4
+
+ global SEV_TYPES
+ global SEV_NONE
+ global SEV_DETAILED
+ global SEV_INFO
+ global SEV_WARN
+ global SEV_CRIT
+ global SEV_FATAL
+ SEV_TYPES = ["none", "DETAILED", "INFO", "WARN", "CRITICAL", "FATAL"]
+ SEV_NONE = 0
+ SEV_DETAILED = 1
+ SEV_INFO = 2
+ SEV_WARN = 3
+ SEV_CRIT = 4
+ SEV_FATAL = 5
+
+ global CODE_GENERAL
+ CODE_GENERAL="100"
+
+ global minimum_severity_to_log
+ minimum_severity_to_log=3
+
+
+ # </logging types and severities>
diff --git a/bin/snmptrapd.py b/bin/snmptrapd.py
new file mode 100644
index 0000000..dde4e39
--- /dev/null
+++ b/bin/snmptrapd.py
@@ -0,0 +1,766 @@
+# ============LICENSE_START=======================================================)
+# org.onap.dcae
+# ================================================================================
+# Copyright (c) 2017-2018 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.
+#
+"""
+dcae_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".
+
+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 [-v]
+:Keywords:
+ onap dcae snmp trap publish dmaap
+"""
+
+__docformat__ = 'restructuredtext'
+
+# basics
+import argparse
+import array
+import asyncio
+from collections import Counter
+import datetime
+import errno
+import inspect
+import json
+import logging
+import logging.handlers
+import os
+import pprint
+import requests
+import re
+import sys
+import signal
+import string
+import socket
+import time
+import traceback
+import unicodedata
+import uuid as uuid_mod
+
+# pysnmp
+from pysnmp.entity import engine, config
+from pysnmp.carrier.asyncio.dgram import udp, udp6
+# from pysnmp.carrier.asyncore.dgram import udp
+from pysnmp.entity.rfc3413 import ntfrcv
+from pysnmp.proto.api import v2c
+
+# dcae_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
+
+from trapd_file_utils import roll_all_logs, open_eelf_logs, roll_file, open_file, close_file
+from trapd_logging import ecomp_logger, stdout_logger
+
+prog_name = os.path.basename(__file__)
+verbose = False
+
+# # # # # # # # # # #
+# fx: usage_err
+# # # # # # # # # # #
+
+
+def usage_err():
+ """
+ Notify of incorrect (argument) usage
+ :Parameters:
+ none
+ :Exceptions:
+ none
+ :Keywords:
+ usage args
+ """
+
+ print('Incorrect usage invoked. Correct usage:')
+ print(' %s [-v]' % prog_name)
+ cleanup_and_exit(1, "undefined")
+
+
+# # # # # # # # # # # # # # # # # # #
+# fx: load_all_configs
+# # # # # # # # # # ## # # # # # # #
+
+
+def load_all_configs(_signum, _frame):
+ """
+ Calls individual functions to read various config files required. This
+ function is called directly (e.g. at startup) and is also registered
+ with signal handling (e.g. kill -sigusr1 <pid>)
+
+ :Parameters:
+ signum and frame (only present when called via signal to running process)
+ :Exceptions:
+ none
+ :Keywords:
+ config files
+ :Variables:
+ yaml_conf_file
+ rs
+ """
+
+ 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, tds.CODE_GENERAL, msg)
+
+ # Initialize dmaap requests session object. Close existing session
+ # if applicable.
+ if tds.http_requ_session is not None:
+ tds.http_requ_session.close()
+
+ tds.http_requ_session = init_session_obj()
+ if tds.http_requ_session == None:
+ msg = "Unable to create new http session - FATAL ERROR, exiting"
+ ecomp_logger(tds.LOG_TYPE_ERROR, tds.SEV_FATAL, tds.CODE_GENERAL, msg)
+ stdout_logger(msg)
+ cleanup_and_exit(1, tds.pid_file_name)
+
+ # re-request config from config binding service
+ # (either broker, or json file override)
+ 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)
+
+
+# # # # # # # # # # # # #
+# fx: log_all_arriving_traps
+# # # # # # # # # # # # #
+
+
+def log_all_arriving_traps():
+
+
+ # roll logs as needed/defined in files.roll_frequency
+ if tds.c_config['files.roll_frequency'] == "minute":
+ curr_minute = datetime.datetime.now().minute
+ if curr_minute != tds.last_minute:
+ roll_all_logs()
+ tds.last_minute = curr_minute
+ elif tds.c_config['files.roll_frequency'] == "hour":
+ curr_hour = datetime.datetime.now().hour
+ if curr_hour != tds.last_hour:
+ roll_all_logs()
+ tds.last_hour = curr_hour
+ else:
+ # otherwise, assume daily roll
+ curr_day = datetime.datetime.now().day
+ if curr_day != tds.last_day:
+ roll_all_logs()
+ tds.last_day = curr_day
+
+ # now log latest 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 %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["sysUptime"],
+ tds.trap_dict["uuid"],
+ tds.all_vb_json_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)
+
+
+# # # # # # # # # # # # #
+# fx: log_published_messages
+# # # # # # # # # # # # #
+
+
+def log_published_messages(_post_data_enclosed):
+
+ # FIXME: should keep data dictionary of Fd's open, and reference those vs.
+ # repeatedly opening append-mode
+
+ msg = "adding trap UUID %s to json log" % tds.trap_dict["uuid"]
+ ecomp_logger(tds.LOG_TYPE_DEBUG, tds.SEV_INFO, tds.CODE_GENERAL, msg)
+
+ try:
+ 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, 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)
+
+
+# # # # # # # # # # # # #
+# fx: post_dmaap
+# # # # # # # # # # # # #
+
+
+def post_dmaap():
+ """
+ Publish trap daata in json format to dmaap
+ :Parameters:
+ :Exceptions:
+ none
+ :Keywords:
+ http post dmaap json message
+ :Variables:
+ """
+
+ http_headers = {"Content-type": "application/json"}
+
+ 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, 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
+ else:
+ # otherwise, add brackets around package
+ post_data_enclosed = '[' + tds.all_traps_str + ']'
+
+ msg = "post_data_enclosed: %s" % (post_data_enclosed)
+ ecomp_logger(tds.LOG_TYPE_DEBUG, tds.SEV_DETAILED, tds.CODE_GENERAL, msg)
+
+ k = 0
+ dmaap_pub_success = False
+
+ while not dmaap_pub_success and k < (int(tds.c_config['publisher.http_retries'])):
+ try:
+ if tds.c_config['streams_publishes']['sec_fault_unsecure']['aaf_username'] == "" or tds.c_config['streams_publishes']['sec_fault_unsecure']['aaf_username'] == 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, 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,
+ timeout=tds.timeout_seconds)
+ 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, 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'],
+ tds.c_config['streams_publishes']['sec_fault_unsecure']['aaf_password']),
+ headers=http_headers,
+ timeout=tds.timeout_seconds)
+
+ if http_resp.status_code == requests.codes.ok:
+ # msg = "%d trap(s) : %s successfully published - response from %s: %d %s" % (traps_since_last_publish, trap_uuids_in_buffer, ((c_config['streams_publishes']['sec_fault_unsecure']['dmaap_info']['topic_url']).split('/')[2][:-5]) ,http_resp.status_code, http_resp.text)
+ msg = "%d trap(s) successfully published: %s" % (tds.traps_since_last_publish, tds.trap_uuids_in_buffer)
+ ecomp_logger(tds.LOG_TYPE_METRICS, tds.SEV_INFO, tds.CODE_GENERAL, msg)
+ log_published_messages(post_data_enclosed)
+ tds.last_pub_time = time.time()
+ dmaap_pub_success = True
+ break
+ else:
+ msg = "Trap(s) %s publish attempt %d returned non-normal: %d %s" % (tds.trap_uuids_in_buffer, k, http_resp.status_code, http_resp.text)
+ ecomp_logger(tds.LOG_TYPE_ERROR, tds.SEV_WARN, tds.CODE_GENERAL, msg)
+
+ except OSError as e:
+ msg = "OS exception while attempting to post %s attempt %s: (%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)
+
+ except requests.exceptions.RequestException as e:
+ msg = "Requests exception while attempting to post %s attempt %d: (%d) %s" % (tds.trap_uuids_in_buffer, int(k), int(e.errno), str(e.strerror))
+ ecomp_logger(tds.LOG_TYPE_ERROR, tds.SEV_WARN, tds.CODE_GENERAL, msg)
+
+ k += 1
+
+ if k < 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, tds.CODE_GENERAL, msg)
+ time.sleep(tds.seconds_between_retries)
+ else:
+ break
+
+ 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)
+
+ # FIXME: 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.first_trap = True
+
+# # # # # # # # # # # # # # # # # # #
+# fx: request_observer for community string rewrite
+# # # # # # # # # # # # # # # # # # #
+def comm_string_rewrite_observer(snmpEngine, execpoint, variables, cbCtx):
+
+ # match ALL community strings
+ if re.match('.*', str(variables['communityName'])):
+ # msg = "Rewriting communityName '%s' from %s into 'public'" % (variables['communityName'], ':'.join([str(x) for x in
+ # variables['transportInformation'][1]]))
+ # ecomp_logger(eelf_debug_fd, eelf_debug_fd, tds.LOG_TYPE_DEBUG, tds.SEV_INFO, tds.CODE_GENERAL, msg)
+ variables['communityName'] = variables['communityName'].clone('public')
+
+# # # # # # # # # # # # # # # # # # #
+# fx: snmp_engine_observer_cb
+# callback for when trap is received
+# # # # # # # # # # # # # # # # # # #
+
+
+def snmp_engine_observer_cb(snmp_engine, execpoint, variables, cbCtx):
+ """
+ Decompose trap attributes and load in dictionary which is later used to
+ create json string for publishing to dmaap.
+ :Parameters:
+ snmp_engine
+ snmp engine created to listen for arriving traps
+ execpoint
+ point in code that snmp_engine_observer_cb was invoked
+ variables
+ trap attributes
+ cbCtx
+ callback context
+ :Exceptions:
+ none
+ :Keywords:
+ UEB non-AAF legacy http post
+ :Variables:
+ """
+
+ # init dictionary on new trap
+ tds.trap_dict = {}
+
+ # assign uuid to trap
+ tds.trap_dict["uuid"] = str(uuid_mod.uuid1())
+
+ # ip and hostname
+ ip_addr_str = str(variables['transportAddress'][0])
+ tds.trap_dict["agent address"] = ip_addr_str
+
+ 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:
+ 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:
+ 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"])
+ tds.trap_dict["community"] = "" # do not include cleartext community in pub
+ tds.trap_dict["community len"] = 0
+
+ # FIXME.CHECK_WITH_DOWNSTREAM_CONSUMERS: get rid of round for millisecond val
+ # epoch_second = int(round(time.time()))
+ epoch_msecond = time.time()
+ epoch_second = int(round(epoch_msecond))
+ if epoch_second == tds.last_epoch_second:
+ tds.traps_in_epoch += 1
+ else:
+ 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_serno'] = int(
+ (str(epoch_second) + str(traps_in_epoch_04d)))
+
+ snmp_version = variables['securityModel']
+ if snmp_version == 1:
+ tds.trap_dict["protocol version"] = "v1"
+ else:
+ if snmp_version == 2:
+ tds.trap_dict["protocol version"] = "v2c"
+ else:
+ if snmp_version == 3:
+ tds.trap_dict["protocol version"] = "v3"
+ else:
+ tds.trap_dict["protocol version"] = "unknown"
+
+ if snmp_version == 3:
+ tds.trap_dict["protocol version"] = "v3"
+ tds.trap_dict["security level"] = str(variables['securityLevel'])
+ tds.trap_dict["context name"] = str(variables['contextName'].prettyPrint())
+ tds.trap_dict["security name"] = str(variables['securityName'])
+ tds.trap_dict["security engine"] = str(
+ variables['contextEngineId'].prettyPrint())
+ tds.trap_dict['time received'] = epoch_msecond
+ tds.trap_dict['trap category'] = (tds.c_config['streams_publishes']['sec_fault_unsecure']['dmaap_info']['topic_url']).split('/')[-1]
+
+
+# # # # # # # # # # # # # # # # # # #
+# fx: request_observer for community string rewrite
+# # # # # # # # # # # # # # # # # # #
+def add_varbind_to_json(vb_idx, vb_oid, vb_type, vb_val):
+ """
+ Called for each varbind, adds individual attributes of varbind instance to
+ vb_json_str. vb_json_str will be added to curr_trap_json_str prior to publish.
+ :Parameters:
+ vb_idx
+ index to specific varbind being processed
+ vb_oid
+ the varbind oid
+ vb_val
+ the value of the varbind
+ :Exceptions:
+ none
+ :Keywords:
+ varbind extract json
+ :Variables:
+ """
+
+ _individual_vb_dict = {}
+
+ if tds.trap_dict["protocol version"] == "v2c":
+ # if v2c and first 2 varbinds, special handling required - e.g. put
+ # in trap_dict, not vb_json_str
+ if vb_idx == 0:
+ tds.trap_dict["sysUptime"] = str(vb_val.prettyPrint())
+ return True
+ else:
+ 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('.') + 1)
+ return True
+ if tds.first_varbind:
+ tds.all_vb_json_str = ', \"varbinds\": ['
+ tds.first_varbind = False
+ else:
+ # all_vb_json_str = ''.join([all_vb_json_str, ' ,'])
+ # all_vb_json_str = "%s ," % all_vb_json_str
+ 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'] = vb_type
+ _individual_vb_dict['varbind_value'] = vb_val.prettyPrint()
+
+ _individual_vb_json_str = json.dumps(_individual_vb_dict)
+
+ # all_vb_json_str = "%s%s" % (all_vb_json_str, individual_vb_json_str)
+ # all_vb_json_str = ''.join([all_vb_json_str, individual_vb_json_str])
+ tds.all_vb_json_str = tds.all_vb_json_str + _individual_vb_json_str
+ return True
+
+
+# Callback function for receiving notifications
+# noinspection PyUnusedLocal,PyUnusedLocal,PyUnusedLocal
+def notif_receiver_cb(snmp_engine, stateReference, contextEngineId, contextName,
+ varBinds, cbCtx):
+ """
+ Callback executed when trap arrives
+ :Parameters:
+ snmp_engine
+ snmp engine created to listen for arriving traps
+ stateReference
+ contextEngineId
+ contextName
+ varBinds
+ trap varbinds - why we are here
+ cbCtx
+ callback context
+ :Exceptions:
+ none
+ :Keywords:
+ callback trap arrival
+ :Variables:
+ """
+
+ msg = "processing varbinds for %s" % (tds.trap_dict["uuid"])
+ ecomp_logger(tds.LOG_TYPE_DEBUG, tds.SEV_DETAILED, tds.CODE_GENERAL, msg)
+
+ # FIXME update reset location when batching publishes
+ vb_idx = 0
+
+ # For reference:
+ #
+ # print('\nvarBinds ==> %s' % (varBinds))
+ #
+ # varBinds ==> [(ObjectName('1.3.6.1.2.1.1.3.0'), TimeTicks(1243175676)),
+ # (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'Fri Aug 11 17:46:01 EDT 2017'))]
+ #
+
+ tds.all_vb_json_str = ""
+ vb_idx = 0
+ tds.first_varbind = True
+
+ # iterate over varbinds, add to json struct
+ for vb_oid, vb_val in varBinds:
+ add_varbind_to_json(vb_idx, vb_oid, vb_val.__class__.__name__, vb_val)
+ vb_idx += 1
+
+ # FIXME: DL back out first 2 varbinds for v2c notifs prior to publishing varbind count
+ # trap_dict["varbind count"] = vb_idx
+ curr_trap_json_str = json.dumps(tds.trap_dict)
+ # now have everything except varbinds in "curr_trap_json_str"
+
+ # if varbinds present - which will almost always be the case - add all_vb_json_str to trap_json_message
+ if vb_idx != 0:
+ # close out vb array
+ # all_vb_json_str += "]"
+ # all_vb_json_str = ''.join([all_vb_json_str, ']'])
+ tds.all_vb_json_str = tds.all_vb_json_str + ']'
+
+ # remove last close bracket from curr_trap_json_str
+ curr_trap_json_str = curr_trap_json_str[:-1]
+
+ # add vb_json_str to payload
+ # curr_trap_json_str += all_vb_json_str
+ # curr_trap_json_str = ''.join([curr_trap_json_str, all_vb_json_str])
+ curr_trap_json_str = curr_trap_json_str + tds.all_vb_json_str
+
+ # add last close brace back in
+ # curr_trap_json_str += "}"
+ # curr_trap_json_str = ''.join([curr_trap_json_str, '}'])
+ 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)
+
+ # now have a complete json message for this trap in "curr_trap_json_str"
+ 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
+ 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
+
+ # always log arriving traps
+ log_all_arriving_traps()
+
+ # publish to dmaap after last varbind is processed
+ if tds.traps_since_last_publish >= 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.CODE_GENERAL, msg)
+ post_dmaap()
+ elif milliseconds_since_last_publish >= 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, tds.CODE_GENERAL, msg)
+ post_dmaap()
+
+
+# # # # # # # # # # # # #
+# Main MAIN Main MAIN
+# # # # # # # # # # # # #
+# parse command line args
+parser = argparse.ArgumentParser(description='Post SNMP traps '
+ 'to message bus')
+parser.add_argument('-v', action="store_true", dest="verbose",
+ 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()
+
+# 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['snmptrap.title'], tds.c_config['snmptrap.version'])
+stdout_logger(msg)
+
+# Avoid this unless needed for testing; it prints sensitive data to log
+#
+# msg = "Running config: "
+# stdout_logger(msg)
+# msg = json.dumps(c_config, sort_keys=False, indent=4)
+# 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 messages will be logged. This can slow things down, use only when needed."
+ tds.minimum_severity_to_log=0
+ stdout_logger(msg)
+
+# name and open arriving trap log
+tds.arriving_traps_filename = tds.c_config['files.runtime_base_dir'] + "/" + tds.c_config['files.log_dir'] + "/" + (tds.c_config['files.arriving_traps_log'])
+tds.arriving_traps_fd = open_file(tds.arriving_traps_filename)
+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"
+tds.json_traps_fd = open_file(tds.json_traps_filename)
+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 autogenernated engineID pre-bound
+# to socket transport dispatcher
+snmp_engine = engine.SnmpEngine()
+
+# # # # # # # # # # # #
+# Transport setup
+# # # # # # # # # # # #
+
+# UDP over IPv4
+# FIXME: add check for presense of ipv4_interface prior to attempting add OR just put entire thing in try/except clause
+try:
+ ipv4_interface = tds.c_config['protocols.ipv4_interface']
+ ipv4_port = tds.c_config['protocols.ipv4_port']
+
+ try:
+ config.addTransport(
+ snmp_engine,
+ udp.domainName + (1,),
+ udp.UdpTransport().openServerMode(
+ (ipv4_interface, ipv4_port))
+ )
+ except Exception as e:
+ msg = "Unable to bind to %s:%s - %s" % (ipv4_interface, ipv4_port, str(e))
+ 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 IPv4, second listening interface/port example if you don't want to listen on all
+# config.addTransport(
+# snmp_engine,
+# udp.domainName + (2,),
+# udp.UdpTransport().openServerMode(('127.0.0.1', 2162))
+# )
+
+
+# UDP over IPv6
+# FIXME: add check for presense of ipv6_interface prior to attempting add OR just put entire thing in try/except clause
+try:
+ ipv6_interface = tds.c_config['protocols.ipv6_interface']
+ ipv6_port = tds.c_config['protocols.ipv6_port']
+
+ try:
+ config.addTransport(
+ snmp_engine,
+ udp6.domainName,
+ udp6.Udp6Transport().openServerMode(
+ (ipv6_interface, ipv6_port))
+ )
+ except Exception as e:
+ msg = "Unable to bind to %s:%s - %s" % (ipv6_interface,ipv6_port, str(e))
+ stdout_logger(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,
+ 'rfc2576.processIncomingMsg:writable'
+)
+
+# register snmp_engine_observer_cb for message arrival
+snmp_engine.observer.registerObserver(
+ snmp_engine_observer_cb,
+ '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()
+except:
+ snmp_engine.observer.unregisterObserver()
+ snmp_engine.transportDispatcher.closeDispatcher()
+ cleanup_and_exit(1, tds.pid_file_name)
diff --git a/bin/snmptrapd.sh b/bin/snmptrapd.sh
new file mode 100755
index 0000000..52f3913
--- /dev/null
+++ b/bin/snmptrapd.sh
@@ -0,0 +1,52 @@
+#!/usr/bin/env bash
+#
+# ============LICENSE_START=======================================================
+# org.onap.dcae
+# ================================================================================
+# Copyright (c) 2017-2018 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.
+#
+
+
+# get to where we are supposed to be for startup
+cd /opt/app/snmptrap/bin
+
+# include path to 3.6+ version of python that has required dependencies included
+export PATH=/opt/app/python-3.6.1/bin:$PATH
+
+# expand search for python modules to include ./mod in runtime dir
+export PYTHONPATH=./mod:./:$PYTHONPATH
+
+# set location of SSL certificates
+export REQUESTS_CA_BUNDLE=/etc/ssl/certs/ca-bundle.crt
+
+# PYTHONUNBUFFERED:
+# set PYTHONUNBUFFERED to a non-empty string to avoid output buffering;
+# comment out for runtime environments/better performance!
+export PYTHONUNBUFFERED="True"
+
+# set location of config broker server overrride IF NEEDED
+#
+export CBS_SIM_JSON=../etc/snmptrapd.json
+
+# want tracing? Use this:
+# python -m trace --trackcalls snmptrapd.py -v
+# want verbose logging? Use this:
+# python snmptrapd.py -v
+# standard startup? Use this:
+# python snmptrapd.py
+python snmptrapd.py -v