diff --git a/dockers/docker-database/critical_processes b/dockers/docker-database/critical_processes index 53a45931dfc9..5fdfd100672d 100644 --- a/dockers/docker-database/critical_processes +++ b/dockers/docker-database/critical_processes @@ -1 +1,2 @@ program:redis +program:redis1 diff --git a/dockers/docker-database/database_config.json.j2 b/dockers/docker-database/database_config.json.j2 index 5ad730f10015..b15dcf26ce0d 100644 --- a/dockers/docker-database/database_config.json.j2 +++ b/dockers/docker-database/database_config.json.j2 @@ -11,6 +11,12 @@ "port": 6380, "unix_socket_path": "/var/run/redis-chassis/redis_chassis.sock", "persistence_for_warm_boot" : "yes" + }, + "redis1":{ + "hostname" : "{{HOST_IP}}", + "port" : 6381, + "unix_socket_path" : "/var/run/redis{{NAMESPACE_ID}}/redis1.sock", + "persistence_for_warm_boot" : "yes" } }, "DATABASES" : { @@ -88,6 +94,11 @@ "id" : 13, "separator": "|", "instance" : "redis_chassis" + }, + "EVENT_DB" : { + "id" : 14, + "separator": "|", + "instance" : "redis1" } }, "VERSION" : "1.0" diff --git a/dockers/docker-eventd/Dockerfile.j2 b/dockers/docker-eventd/Dockerfile.j2 new file mode 100644 index 000000000000..7005f9f9367e --- /dev/null +++ b/dockers/docker-eventd/Dockerfile.j2 @@ -0,0 +1,27 @@ +FROM docker-config-engine-buster + +ARG docker_container_name +RUN [ -f /etc/rsyslog.conf ] && sed -ri "s/%syslogtag%/$docker_container_name#%syslogtag%/;" /etc/rsyslog.conf + +## Make apt-get non-interactive +ENV DEBIAN_FRONTEND=noninteractive + +COPY \ +{% for deb in docker_eventd_debs.split(' ') -%} +debs/{{ deb }}{{' '}} +{%- endfor -%} +debs/ + +RUN dpkg -i \ +{% for deb in docker_eventd_debs.split(' ') -%} +debs/{{ deb }}{{' '}} +{%- endfor %} + +COPY ["docker-init.sh", "files/supervisor-proc-exit-listener", "/usr/bin/"] +COPY ["supervisord.conf", "/etc/supervisor/conf.d/"] +COPY ["critical_processes", "/etc/supervisor"] + +RUN apt-get clean -y; apt-get autoclean -y; apt-get autoremove -y +RUN rm -rf /debs + +ENTRYPOINT ["/usr/bin/docker-init.sh"] diff --git a/dockers/docker-eventd/critical_processes b/dockers/docker-eventd/critical_processes new file mode 100644 index 000000000000..8ff28edbc148 --- /dev/null +++ b/dockers/docker-eventd/critical_processes @@ -0,0 +1 @@ +program:eventd diff --git a/dockers/docker-eventd/docker-init.sh b/dockers/docker-eventd/docker-init.sh new file mode 100755 index 000000000000..4bdb55360c0f --- /dev/null +++ b/dockers/docker-eventd/docker-init.sh @@ -0,0 +1,8 @@ +#!/usr/bin/env bash + +sonic-db-cli EVENT_DB config set notify-keyspace-events AKE + +cp /var/evprofile/default.json /etc/evprofile/default.json + +exec /usr/local/bin/supervisord + diff --git a/dockers/docker-eventd/supervisord.conf b/dockers/docker-eventd/supervisord.conf new file mode 100644 index 000000000000..c4460b4f6a4b --- /dev/null +++ b/dockers/docker-eventd/supervisord.conf @@ -0,0 +1,40 @@ +[supervisord] +logfile_maxbytes=1MB +logfile_backups=2 +nodaemon=true + +[eventlistener:dependent-startup] +command=python3 -m supervisord_dependent_startup +autostart=true +autorestart=unexpected +startretries=0 +exitcodes=0,3 +events=PROCESS_STATE +buffer_size=1024 + +[eventlistener:supervisor-proc-exit-listener] +command=/usr/bin/supervisor-proc-exit-listener --container-name eventd +events=PROCESS_STATE_EXITED,PROCESS_STATE_RUNNING +autostart=true +autorestart=unexpected +buffer_size=1024 + +[program:rsyslogd] +command=/usr/sbin/rsyslogd -n -iNONE +priority=1 +autostart=false +startsecs=1 +stdout_logfile=syslog +stderr_logfile=syslog +dependent_startup=true + +[program:eventd] +command=/usr/bin/eventd +priority=2 +autostart=false +autorestart=false +stdout_logfile=syslog +stderr_logfile=syslog +dependent_startup=true +dependent_startup_wait_for=rsyslogd:running + diff --git a/dockers/docker-sonic-mgmt-framework/Dockerfile.j2 b/dockers/docker-sonic-mgmt-framework/Dockerfile.j2 index 50854949c893..1f1aed3d186e 100644 --- a/dockers/docker-sonic-mgmt-framework/Dockerfile.j2 +++ b/dockers/docker-sonic-mgmt-framework/Dockerfile.j2 @@ -7,7 +7,7 @@ RUN [ -f /etc/rsyslog.conf ] && sed -ri "s/%syslogtag%/$docker_container_name#%s ENV DEBIAN_FRONTEND=noninteractive RUN apt-get update && \ - apt-get install -y g++ python3-dev libxml2 libcurl3-gnutls libcjson-dev + apt-get install -y g++ python3-dev libxml2 libcurl3-gnutls libcjson1 RUN pip3 install connexion==2.7.0 \ setuptools==21.0.0 \ diff --git a/files/build_templates/docker_image_ctl.j2 b/files/build_templates/docker_image_ctl.j2 index 7b4f91f3d5d0..214fa2e528a3 100644 --- a/files/build_templates/docker_image_ctl.j2 +++ b/files/build_templates/docker_image_ctl.j2 @@ -88,9 +88,14 @@ function preStartAction() # Load redis content from /host/warmboot/dump.rdb docker cp $WARM_DIR/dump.rdb database$DEV:/var/lib/redis/dump.rdb else + COLD_DIR=/host/coldboot # Create an emtpy file and overwrite any RDB if already there - echo -n > /tmp/dump.rdb - docker cp /tmp/dump.rdb database$DEV:/var/lib/redis/ + if [[ -s $COLD_DIR/dump.rdb ]]; then + docker cp $COLD_DIR/dump.rdb database$DEV:/var/lib/redis/dump.rdb + else + echo -n > /tmp/dump.rdb + docker cp /tmp/dump.rdb database$DEV:/var/lib/redis/ + fi fi fi {%- elif docker_container_name == "snmp" %} diff --git a/files/build_templates/eventd.service.j2 b/files/build_templates/eventd.service.j2 new file mode 100644 index 000000000000..36635a515b3d --- /dev/null +++ b/files/build_templates/eventd.service.j2 @@ -0,0 +1,16 @@ +[Unit] +Description=Eventd container +Requires=database.service +After=database.service +Before=ntp-config.service +StartLimitIntervalSec=1200 +StartLimitBurst=20 + +[Service] +ExecStartPre=/usr/bin/{{docker_container_name}}.sh start +ExecStart=/usr/bin/{{docker_container_name}}.sh wait +ExecStop=/usr/bin/{{docker_container_name}}.sh stop +Restart=always + +[Install] +WantedBy=multi-user.target diff --git a/files/build_templates/init_cfg.json.j2 b/files/build_templates/init_cfg.json.j2 index 76f769431058..bec4636e2a22 100644 --- a/files/build_templates/init_cfg.json.j2 +++ b/files/build_templates/init_cfg.json.j2 @@ -28,6 +28,7 @@ ("snmp", "enabled", true, "enabled"), ("swss", "enabled", false, "enabled"), ("syncd", "enabled", false, "enabled"), + ("eventd", "enabled", false, "enabled"), ("teamd", "enabled", false, "enabled")] %} {%- if sonic_asic_platform == "vs" %}{% do features.append(("gbsyncd", "enabled", false, "enabled")) %}{% endif %} {%- if include_iccpd == "y" %}{% do features.append(("iccpd", "disabled", false, "enabled")) %}{% endif %} diff --git a/rules/docker-eventd.dep b/rules/docker-eventd.dep new file mode 100644 index 000000000000..7047ba0350b0 --- /dev/null +++ b/rules/docker-eventd.dep @@ -0,0 +1,11 @@ +DPATH := $($(DOCKER_EVENTD)_PATH) +DEP_FILES := $(SONIC_COMMON_FILES_LIST) rules/docker-eventd.mk rules/docker-eventd.dep +DEP_FILES += $(SONIC_COMMON_BASE_FILES_LIST) +DEP_FILES += $(shell git ls-files $(DPATH)) + +$(DOCKER_EVENTD)_CACHE_MODE := GIT_CONTENT_SHA +$(DOCKER_EVENTD)_DEP_FLAGS := $(SONIC_COMMON_FLAGS_LIST) +$(DOCKER_EVENTD)_DEP_FILES := $(DEP_FILES) + +$(eval $(call add_dbg_docker,$(DOCKER_EVENTD),$(DOCKER_EVENTD_DBG))) + diff --git a/rules/docker-eventd.mk b/rules/docker-eventd.mk new file mode 100644 index 000000000000..3904441de72d --- /dev/null +++ b/rules/docker-eventd.mk @@ -0,0 +1,31 @@ +# Docker image for EVENTD + +DOCKER_EVENTD_STEM = docker-eventd +DOCKER_EVENTD = $(DOCKER_EVENTD_STEM).gz +DOCKER_EVENTD_DBG = $(DOCKER_EVENTD_STEM)-$(DBG_IMAGE_MARK).gz + +$(DOCKER_EVENTD)_PATH = $(DOCKERS_PATH)/$(DOCKER_EVENTD_STEM) + +$(DOCKER_EVENTD)_DEPENDS += $(EVENTD) $(LIBSWSSCOMMON) $(LIBEVENTNOTIFY) +$(DOCKER_EVENTD)_DBG_DEPENDS = $($(DOCKER_CONFIG_ENGINE_BUSTER)_DBG_DEPENDS) +$(DOCKER_EVENTD)_DBG_DEPENDS += $(EVENTD) $(LIBSWSSCOMMON) $(LIBEVENTNOTIFY) +$(DOCKER_EVENTD)_DBG_IMAGE_PACKAGES = $($(DOCKER_CONFIG_ENGINE_BUSTER)_DBG_IMAGE_PACKAGES) + +SONIC_DOCKER_IMAGES += $(DOCKER_EVENTD) +#SONIC_STRETCH_DOCKERS += $(DOCKER_EVENTD) +SONIC_INSTALL_DOCKER_IMAGES += $(DOCKER_EVENTD) + +SONIC_DOCKER_DBG_IMAGES += $(DOCKER_EVENTD_DBG) +#SONIC_STRETCH_DBG_DOCKERS += $(DOCKER_EVENTD_DBG) +SONIC_INSTALL_DOCKER_DBG_IMAGES += $(DOCKER_EVENTD_DBG) + +$(DOCKER_EVENTD)_LOAD_DOCKERS = $(DOCKER_CONFIG_ENGINE_BUSTER) + +$(DOCKER_EVENTD)_CONTAINER_NAME = eventd +$(DOCKER_EVENTD)_RUN_OPT += --net=host --privileged -t +$(DOCKER_EVENTD)_RUN_OPT += -v /etc/sonic/:/etc/sonic/:ro +$(DOCKER_EVENTD)_RUN_OPT += -v /etc/evprofile:/etc/evprofile:rw +$(DOCKER_EVENTD)_RUN_OPT += -v /host/warmboot:/var/warmboot + +$(DOCKER_EVENTD)_FILES += $(SUPERVISOR_PROC_EXIT_LISTENER_SCRIPT) + diff --git a/rules/docker-sonic-mgmt-framework.mk b/rules/docker-sonic-mgmt-framework.mk index ef1d55990816..08093d2d86a8 100644 --- a/rules/docker-sonic-mgmt-framework.mk +++ b/rules/docker-sonic-mgmt-framework.mk @@ -31,6 +31,7 @@ endif $(DOCKER_MGMT_FRAMEWORK)_CONTAINER_NAME = mgmt-framework $(DOCKER_MGMT_FRAMEWORK)_RUN_OPT += --privileged -t $(DOCKER_MGMT_FRAMEWORK)_RUN_OPT += -v /etc/sonic:/etc/sonic:ro +$(DOCKER_MGMT_FRAMEWORK)_RUN_OPT += -v /etc/evprofile:/etc/evprofile:ro $(DOCKER_MGMT_FRAMEWORK)_RUN_OPT += -v /etc:/host_etc:ro $(DOCKER_MGMT_FRAMEWORK)_RUN_OPT += -v /var/run/dbus:/var/run/dbus:rw $(DOCKER_MGMT_FRAMEWORK)_RUN_OPT += --mount type=bind,source="/var/platform/",target="/mnt/platform/" diff --git a/rules/eventd.dep b/rules/eventd.dep new file mode 100644 index 000000000000..df8d9de4f6c3 --- /dev/null +++ b/rules/eventd.dep @@ -0,0 +1,11 @@ +SPATH := $($(EVENTD)_SRC_PATH) +DEP_FILES := $(SONIC_COMMON_FILES_LIST) rules/eventd.mk rules/eventd.dep +DEP_FILES += $(SONIC_COMMON_BASE_FILES_LIST) +SMDEP_FILES := $(addprefix $(SPATH)/,$(shell cd $(SPATH) && git ls-files)) + +$(EVENTD)_CACHE_MODE := GIT_CONTENT_SHA +$(EVENTD)_DEP_FLAGS := $(SONIC_COMMON_FLAGS_LIST) +$(EVENTD)_DEP_FILES := $(DEP_FILES) +$(EVENTD)_SMDEP_FILES := $(SMDEP_FILES) +$(EVENTD)_SMDEP_PATHS := $(SPATH) + diff --git a/rules/eventd.mk b/rules/eventd.mk new file mode 100644 index 000000000000..52d3baed46c1 --- /dev/null +++ b/rules/eventd.mk @@ -0,0 +1,18 @@ +# EVENTD package +# +EVENTD_VERSION = 1.0.0 +export EVENTD_VERSION + +EVENTD = eventd_$(EVENTD_VERSION)_amd64.deb +$(EVENTD)_SRC_PATH = $(SRC_PATH)/sonic-eventd +$(EVENTD)_DEPENDS += $(LIBSWSSCOMMON_DEV) $(LIBEVENTNOTIFY_DEV) +$(EVENTD)_RDEPENDS += $(LIBSWSSCOMMON) $(LIBEVENTNOTIFY) +SONIC_DPKG_DEBS += $(EVENTD) + +EVENTD_DBG = eventd-dbg_1.0.0_amd64.deb +$(EVENTD_DBG)_DEPENDS += $(EVENTD) +$(EVENTD_DBG)_RDEPENDS += $(EVENTD) +$(eval $(call add_derived_package,$(EVENTD),$(EVENTD_DBG))) + +export EVENTD + diff --git a/rules/libeventnotify.dep b/rules/libeventnotify.dep new file mode 100644 index 000000000000..d16b711000c7 --- /dev/null +++ b/rules/libeventnotify.dep @@ -0,0 +1,11 @@ +SPATH := $($(LIBEVENTNOTIFY)_SRC_PATH) +DEP_FILES := $(SONIC_COMMON_FILES_LIST) rules/libeventnotify.mk rules/libeventnotify.dep +DEP_FILES += $(SONIC_COMMON_BASE_FILES_LIST) +SMDEP_FILES := $(addprefix $(SPATH)/,$(shell cd $(SPATH) && git ls-files)) + +$(LIBEVENTNOTIFY)_CACHE_MODE := GIT_CONTENT_SHA +$(LIBEVENTNOTIFY)_DEP_FLAGS := $(SONIC_COMMON_FLAGS_LIST) +$(LIBEVENTNOTIFY)_DEP_FILES := $(DEP_FILES) +$(LIBEVENTNOTIFY)_SMDEP_FILES := $(SMDEP_FILES) +$(LIBEVENTNOTIFY)_SMDEP_PATHS := $(SPATH) + diff --git a/rules/libeventnotify.mk b/rules/libeventnotify.mk new file mode 100644 index 000000000000..4377c5dd3168 --- /dev/null +++ b/rules/libeventnotify.mk @@ -0,0 +1,13 @@ +# SONiC event notfiy library + +LIBEVENTNOTIFY_VERSION = 1.0.0 +LIBEVENTNOTIFY = libeventnotify_$(LIBEVENTNOTIFY_VERSION)_amd64.deb +$(LIBEVENTNOTIFY)_DEPENDS += $(LIBSWSSCOMMON_DEV) +$(LIBEVENTNOTIFY)_RDEPENDS += $(LIBSWSSCOMMON) + +$(LIBEVENTNOTIFY)_SRC_PATH = $(SRC_PATH)/sonic-eventd/lib +SONIC_DPKG_DEBS += $(LIBEVENTNOTIFY) + +LIBEVENTNOTIFY_DEV = libeventnotify-dev_$(LIBEVENTNOTIFY_VERSION)_amd64.deb +$(eval $(call add_derived_package,$(LIBEVENTNOTIFY),$(LIBEVENTNOTIFY_DEV))) + diff --git a/src/sonic-eventd/Makefile.am b/src/sonic-eventd/Makefile.am new file mode 100644 index 000000000000..3f06593df83a --- /dev/null +++ b/src/sonic-eventd/Makefile.am @@ -0,0 +1,4 @@ +ACLOCAL_AMFLAGS = -I m4 +#INCLUDES = -I $(top_srcdir) +SUBDIRS = src + diff --git a/src/sonic-eventd/autogen.sh b/src/sonic-eventd/autogen.sh new file mode 100755 index 000000000000..c8d0bbe4a251 --- /dev/null +++ b/src/sonic-eventd/autogen.sh @@ -0,0 +1,6 @@ +#!/bin/bash + +libtoolize --force --copy && +autoreconf --force --install -I m4 +rm -Rf autom4te.cache + diff --git a/src/sonic-eventd/configure.ac b/src/sonic-eventd/configure.ac new file mode 100644 index 000000000000..b415ad674e52 --- /dev/null +++ b/src/sonic-eventd/configure.ac @@ -0,0 +1,50 @@ +AC_INIT([sonic-eventd],[1.0.0]) +AC_CONFIG_SRCDIR([]) +AC_CONFIG_AUX_DIR(config) +AM_CONFIG_HEADER(config.h) +AM_INIT_AUTOMAKE([foreign]) +AC_LANG_C +AC_LANG([C++]) +AC_PROG_CC +AC_PROG_CXX +AC_PROG_LIBTOOL +AC_HEADER_STDC + +AC_ARG_ENABLE(debug, +[ --enable-debug Compile with debugging flags], +[case "${enableval}" in + yes) debug=true ;; + no) debug=false ;; + *) AC_MSG_ERROR(bad value ${enableval} for --enable-debug) ;; +esac],[debug=false]) +AM_CONDITIONAL(DEBUG, test x$debug = xtrue) + +AC_ARG_ENABLE(gtest, +[ --enable-gtest Compile with googletest flags], +[case "${enableval}" in + yes) gtest=true ;; + no) gtest=false ;; + *) AC_MSG_ERROR(bad value ${enableval} for --enable-gtest) ;; +esac],[gtest=false]) +AM_CONDITIONAL(GTEST, test x$gtest = xtrue) + +CFLAGS_COMMON="-std=c++11 -Wall -fPIC -Wno-write-strings -I/usr/include/swss -I/usr/include" + +CFLAGS_COMMON+=" -Werror" + +AC_SUBST(CFLAGS_COMMON) + +dnl --------------- +dnl dlopen & dlinfo +dnl --------------- +AC_SEARCH_LIBS([dlopen], [dl dld], [], [ + AC_MSG_ERROR([unable to find the dlopen()]) +]) + +AC_CONFIG_FILES([ + src/Makefile + Makefile +]) + +AC_OUTPUT + diff --git a/src/sonic-eventd/debian/changelog b/src/sonic-eventd/debian/changelog new file mode 100644 index 000000000000..bd71a2619f89 --- /dev/null +++ b/src/sonic-eventd/debian/changelog @@ -0,0 +1,6 @@ +sonic (1.0.0) stable; urgency=medium + + * Initial release. + + -- Eventd Thu, 9 Apr 2021 12:00:00 -0800 + diff --git a/src/sonic-eventd/debian/compat b/src/sonic-eventd/debian/compat new file mode 100644 index 000000000000..ec635144f600 --- /dev/null +++ b/src/sonic-eventd/debian/compat @@ -0,0 +1 @@ +9 diff --git a/src/sonic-eventd/debian/control b/src/sonic-eventd/debian/control new file mode 100644 index 000000000000..36c33aad9397 --- /dev/null +++ b/src/sonic-eventd/debian/control @@ -0,0 +1,19 @@ +Source: sonic +Maintainer: Dell +Section: net +Priority: optional +Build-Depends: dh-exec (>=0.3), debhelper (>= 9), autotools-dev +Standards-Version: 1.0.0 + +Package: eventd +Architecture: any +Depends: ${shlibs:Depends} +Description: This package contains Eventd for SONiC project. + +Package: eventd-dbg +Architecture: any +Section: debug +Priority: extra +Depends: stp (=${binary:Version}) +Description: debugging symbols for eventd + diff --git a/src/sonic-eventd/debian/eventd.dirs b/src/sonic-eventd/debian/eventd.dirs new file mode 100644 index 000000000000..0bf940b3a49b --- /dev/null +++ b/src/sonic-eventd/debian/eventd.dirs @@ -0,0 +1 @@ +/usr/lib diff --git a/src/sonic-eventd/debian/eventd.install b/src/sonic-eventd/debian/eventd.install new file mode 100644 index 000000000000..ca3bc3904510 --- /dev/null +++ b/src/sonic-eventd/debian/eventd.install @@ -0,0 +1,2 @@ +/etc/eventd.json +/var/evprofile/default.json diff --git a/src/sonic-eventd/debian/rules b/src/sonic-eventd/debian/rules new file mode 100755 index 000000000000..707d224ea242 --- /dev/null +++ b/src/sonic-eventd/debian/rules @@ -0,0 +1,35 @@ +#!/usr/bin/make -f +# See debhelper(7) (uncomment to enable) +# output every command that modifies files on the build system. +#export DH_VERBOSE = 1 + +# see EXAMPLES in dpkg-buildflags(1) and read /usr/share/dpkg/* +DPKG_EXPORT_BUILDFLAGS = 1 +include /usr/share/dpkg/default.mk + +# see FEATURE AREAS in dpkg-buildflags(1) +#export DEB_BUILD_MAINT_OPTIONS = hardening=+all + +# see ENVIRONMENT in dpkg-buildflags(1) +# package maintainers to append CFLAGS +#export DEB_CFLAGS_MAINT_APPEND = -Wall -pedantic +# package maintainers to append LDFLAGS +#export DEB_LDFLAGS_MAINT_APPEND = -Wl,--as-needed + + +# main packaging script based on dh7 syntax +%: + dh $@ --with autotools-dev + +# dh_make generated override targets +# This is example for Cmake (See https://bugs.debian.org/641051 ) +#override_dh_auto_configure: +# dh_auto_configure -- \ +# -DCMAKE_LIBRARY_PATH=$(DEB_HOST_MULTIARCH) + +override_dh_auto_install: + dh_auto_install --destdir=debian/eventd + +override_dh_strip: + dh_strip --dbg-package=eventd-dbg + diff --git a/src/sonic-eventd/etc/eventd.json b/src/sonic-eventd/etc/eventd.json new file mode 100644 index 000000000000..d1fc54479037 --- /dev/null +++ b/src/sonic-eventd/etc/eventd.json @@ -0,0 +1,6 @@ +{ + "__README__": "Specify size of event history table. Whichever limit is hit first, eventd wraps event history table around and deletes older records.", + "max-records": 40000, + "max-days": 30 +} + diff --git a/src/sonic-eventd/include/eventconsume.h b/src/sonic-eventd/include/eventconsume.h new file mode 100644 index 000000000000..18654dba8299 --- /dev/null +++ b/src/sonic-eventd/include/eventconsume.h @@ -0,0 +1,59 @@ +#ifndef __EVENTCONSUME_H__ +#define __EVENTCONSUME_H__ + +#include +#include +#include +#include +#include +#include +#include +#include +#include "dbconnector.h" +#include "subscriberstatetable.h" + +namespace evt { +using namespace swss; +using namespace std; + +class EventConsume +{ +public: + EventConsume(DBConnector *dbConn); + ~EventConsume(); + void run(); + void read_eventd_config(bool read_all=true); + uint64_t get_seq_id(); + +private: + Table m_eventTable; + Table m_alarmTable; + Table m_eventStatsTable; + Table m_alarmStatsTable; + SubscriberStateTable m_consumerTable; + SubscriberStateTable m_evprofileTable; + Table m_eventPubSubTable; + u_int32_t days, count; + + void handle_notification(std::deque kco); + void handle_custom_evprofile(std::deque); + void read_events(); + void updateAlarmStatistics(string ev_sev, string ev_act); + void updateEventStatistics(bool is_add, bool is_alarm, bool is_ack, bool is_clear); + void read_config_and_purge(); + void update_events(string seq_id, string ts, vector vec); + void purge_events(); + void modifyEventStats(string seq_id); + void clearAckAlarmStatistic(); + void resetAlarmStats(int, int, int, int, int, int); + void fetchFieldValues(const vector& , vector &, string &, string &, string &, string &, string &); + bool isFloodedEvent(string, string, string, string); + bool staticInfoExists(string &, string &, string &, string &, vector &); + bool udpateLocalCacheAndAlarmTable(string, bool &); + void initStats(); + void updateAckInfo(bool, string, string, string, string); + void fetchRaiseInfo(vector &, string, string &, string &, string &, string &, string &); +}; +} + +#endif /* __EVENTCONSUME_H__ */ diff --git a/src/sonic-eventd/include/eventutils.h b/src/sonic-eventd/include/eventutils.h new file mode 100644 index 000000000000..bff76ce4da4e --- /dev/null +++ b/src/sonic-eventd/include/eventutils.h @@ -0,0 +1,60 @@ +#ifndef __EVENTUTILS_H__ +#define __EVENTUTILS_H__ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace evtutils { +using namespace std; + +const string EVENT_SEVERITY_CRITICAL_STR = "CRITICAL"; +const string EVENT_SEVERITY_MAJOR_STR = "MAJOR"; +const string EVENT_SEVERITY_MINOR_STR = "MINOR"; +const string EVENT_SEVERITY_WARNING_STR = "WARNING"; +const string EVENT_SEVERITY_INFORMATIONAL_STR = "INFORMATIONAL"; + +const string EVENT_ENABLE_TRUE_STR = "true"; +const string EVENT_ENABLE_FALSE_STR = "false"; + +const string EVENT_ACTION_RAISE_STR = "RAISE"; +const string EVENT_ACTION_CLEAR_STR = "CLEAR"; +const string EVENT_ACTION_ACK_STR = "ACKNOWLEDGE"; +const string EVENT_ACTION_UNACK_STR = "UNACKNOWLEDGE"; + +constexpr char EVENTD_PROFILE_DIR[] = "/etc/evprofile/"; +constexpr char EVENTD_DEFAULT_MAP_FILE[] = "/etc/evprofile/default.json"; +constexpr char EVENTD_PROFILE_SYMLINK[] = "/etc/evprofile/.current"; + +constexpr size_t EHT_MAX_ELEMS = 40000; +constexpr size_t EHT_MAX_DAYS = 30; +constexpr char EVENTD_CONF_FILE[] = "/etc/eventd.json"; + +typedef struct EventInfo_t { + string severity; + string enable; + string static_event_msg; +} EventInfo; + +//unordered_map static_event_table; +typedef unordered_map EventMap; + +bool isValidSeverity(string severityStr); +bool isValidEnable(string enableStr); +bool parse_config(const char *filename, unsigned int& days, unsigned int& count); +bool parse(const char *filename, EventMap& tmp_event_table); +void merge(EventMap& static_event_table, EventMap &profile_map); +void create_symlink(); +string getTimeTicks(); + +} + +#endif diff --git a/src/sonic-eventd/include/loghandler.h b/src/sonic-eventd/include/loghandler.h new file mode 100644 index 000000000000..1e98a940152f --- /dev/null +++ b/src/sonic-eventd/include/loghandler.h @@ -0,0 +1,5 @@ +#include +extern "C" void openSyslog(); +extern "C" void writeToSyslog(std::string ev_id, int ev_sev, std::string ev_type, std::string ev_act, std::string ev_msg, std::string ev_static_msg); +extern "C" void closeSyslog(); + diff --git a/src/sonic-eventd/lib/Makefile.am b/src/sonic-eventd/lib/Makefile.am new file mode 100644 index 000000000000..5ac634e08fa7 --- /dev/null +++ b/src/sonic-eventd/lib/Makefile.am @@ -0,0 +1,5 @@ +ACLOCAL_AMFLAGS = -I m4 +#INCLUDES = -I $(top_srcdir) +SUBDIRS = src + + diff --git a/src/sonic-eventd/lib/autogen.sh b/src/sonic-eventd/lib/autogen.sh new file mode 100755 index 000000000000..c8d0bbe4a251 --- /dev/null +++ b/src/sonic-eventd/lib/autogen.sh @@ -0,0 +1,6 @@ +#!/bin/bash + +libtoolize --force --copy && +autoreconf --force --install -I m4 +rm -Rf autom4te.cache + diff --git a/src/sonic-eventd/lib/configure.ac b/src/sonic-eventd/lib/configure.ac new file mode 100644 index 000000000000..624608c31085 --- /dev/null +++ b/src/sonic-eventd/lib/configure.ac @@ -0,0 +1,50 @@ +AC_INIT([libeventnotify],[1.0.0]) +AC_CONFIG_SRCDIR([]) +AC_CONFIG_AUX_DIR(config) +AM_CONFIG_HEADER(config.h) +AM_INIT_AUTOMAKE([foreign]) +AC_LANG_C +AC_LANG([C++]) +AC_PROG_CC +AC_PROG_CXX +AC_PROG_LIBTOOL +AC_HEADER_STDC + +AC_ARG_ENABLE(debug, +[ --enable-debug Compile with debugging flags], +[case "${enableval}" in + yes) debug=true ;; + no) debug=false ;; + *) AC_MSG_ERROR(bad value ${enableval} for --enable-debug) ;; +esac],[debug=false]) +AM_CONDITIONAL(DEBUG, test x$debug = xtrue) + +AC_ARG_ENABLE(gtest, +[ --enable-gtest Compile with googletest flags], +[case "${enableval}" in + yes) gtest=true ;; + no) gtest=false ;; + *) AC_MSG_ERROR(bad value ${enableval} for --enable-gtest) ;; +esac],[gtest=false]) +AM_CONDITIONAL(GTEST, test x$gtest = xtrue) + +CFLAGS_COMMON="-std=c++11 -Wall -fPIC -Wno-write-strings -I/usr/include/swss -I/usr/include" + +CFLAGS_COMMON+=" -Werror" + +AC_SUBST(CFLAGS_COMMON) + +dnl --------------- +dnl dlopen & dlinfo +dnl --------------- +AC_SEARCH_LIBS([dlopen], [dl dld], [], [ + AC_MSG_ERROR([unable to find the dlopen()]) +]) + +AC_CONFIG_FILES([ + src/Makefile + Makefile +]) + +AC_OUTPUT + diff --git a/src/sonic-eventd/lib/debian/changelog b/src/sonic-eventd/lib/debian/changelog new file mode 100644 index 000000000000..0f2ff23294dc --- /dev/null +++ b/src/sonic-eventd/lib/debian/changelog @@ -0,0 +1,5 @@ +sonic (1.0.0) stable; urgency=medium + + * Initial release. + + -- Eventd Thu, 9 Apr 2021 12:00:00 -0800 diff --git a/src/sonic-eventd/lib/debian/compat b/src/sonic-eventd/lib/debian/compat new file mode 100644 index 000000000000..ec635144f600 --- /dev/null +++ b/src/sonic-eventd/lib/debian/compat @@ -0,0 +1 @@ +9 diff --git a/src/sonic-eventd/lib/debian/control b/src/sonic-eventd/lib/debian/control new file mode 100644 index 000000000000..fd52acf007c6 --- /dev/null +++ b/src/sonic-eventd/lib/debian/control @@ -0,0 +1,17 @@ +Source: sonic +Maintainer: Dell +Section: net +Priority: optional +Build-Depends: dh-exec (>=0.3), debhelper (>= 9), autotools-dev +Standards-Version: 1.0.0 + +Package: libeventnotify +Architecture: any +Section: libs +Description: This package contains Event notify API. + +Package: libeventnotify-dev +Architecture: any +Depends: libeventnotify (= ${binary:Version}) +Section: libdevel +Description: This package contains development files for Event Notify diff --git a/src/sonic-eventd/lib/debian/libeventnotify-dev.dirs b/src/sonic-eventd/lib/debian/libeventnotify-dev.dirs new file mode 100644 index 000000000000..44188162ec7a --- /dev/null +++ b/src/sonic-eventd/lib/debian/libeventnotify-dev.dirs @@ -0,0 +1,2 @@ +usr/lib +usr/include diff --git a/src/sonic-eventd/lib/debian/libeventnotify-dev.install b/src/sonic-eventd/lib/debian/libeventnotify-dev.install new file mode 100644 index 000000000000..bf6d08a7cbba --- /dev/null +++ b/src/sonic-eventd/lib/debian/libeventnotify-dev.install @@ -0,0 +1 @@ +include/*.h usr/include diff --git a/src/sonic-eventd/lib/debian/libeventnotify-dev.links b/src/sonic-eventd/lib/debian/libeventnotify-dev.links new file mode 100644 index 000000000000..df5d23d95e98 --- /dev/null +++ b/src/sonic-eventd/lib/debian/libeventnotify-dev.links @@ -0,0 +1,2 @@ +#! /usr/bin/dh-exec +/usr/lib/x86_64-linux-gnu/libeventnotify.so.0 /usr/lib/x86_64-linux-gnu/libeventnotify.so diff --git a/src/sonic-eventd/lib/debian/libeventnotify.dirs b/src/sonic-eventd/lib/debian/libeventnotify.dirs new file mode 100644 index 000000000000..0bf940b3a49b --- /dev/null +++ b/src/sonic-eventd/lib/debian/libeventnotify.dirs @@ -0,0 +1 @@ +/usr/lib diff --git a/src/sonic-eventd/lib/debian/libeventnotify.install b/src/sonic-eventd/lib/debian/libeventnotify.install new file mode 100644 index 000000000000..b4e9de2503fb --- /dev/null +++ b/src/sonic-eventd/lib/debian/libeventnotify.install @@ -0,0 +1 @@ +usr/lib/*/libeventnotify*.so.* diff --git a/src/sonic-eventd/lib/debian/rules b/src/sonic-eventd/lib/debian/rules new file mode 100755 index 000000000000..638c6a4dec07 --- /dev/null +++ b/src/sonic-eventd/lib/debian/rules @@ -0,0 +1,31 @@ +#!/usr/bin/make -f +# See debhelper(7) (uncomment to enable) +# output every command that modifies files on the build system. +#export DH_VERBOSE = 1 + +# see EXAMPLES in dpkg-buildflags(1) and read /usr/share/dpkg/* +DPKG_EXPORT_BUILDFLAGS = 1 +include /usr/share/dpkg/default.mk + +# see FEATURE AREAS in dpkg-buildflags(1) +#export DEB_BUILD_MAINT_OPTIONS = hardening=+all + +# see ENVIRONMENT in dpkg-buildflags(1) +# package maintainers to append CFLAGS +#export DEB_CFLAGS_MAINT_APPEND = -Wall -pedantic +# package maintainers to append LDFLAGS +#export DEB_LDFLAGS_MAINT_APPEND = -Wl,--as-needed + + +# main packaging script based on dh7 syntax +%: + dh $@ --with autotools-dev + +# dh_make generated override targets +# This is example for Cmake (See https://bugs.debian.org/641051 ) +#override_dh_auto_configure: +# dh_auto_configure -- \ +# -DCMAKE_LIBRARY_PATH=$(DEB_HOST_MULTIARCH) + +override_dh_auto_install: + dh_auto_install diff --git a/src/sonic-eventd/lib/include/eventnotify.h b/src/sonic-eventd/lib/include/eventnotify.h new file mode 100644 index 000000000000..ad76d956b313 --- /dev/null +++ b/src/sonic-eventd/lib/include/eventnotify.h @@ -0,0 +1,44 @@ +#ifndef SWSS_COMMON_EVENTNOTIFY_H +#define SWSS_COMMON_EVENTNOTIFY_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "dbconnector.h" +#include "table.h" +using namespace swss; +namespace alertmgr { + +class EventNotify +{ +public: + static EventNotify *getInstance(); + void _send_event(const char *ev_name_p, const char *source_p, int action, const char *fmt, ...); +private: + EventNotify() = default; + ~EventNotify(); + EventNotify(DBConnector* stateDbConnector); + EventNotify(const EventNotify&); + static EventNotify *m_pInstance; + static DBConnector *p_state_db; + Table m_statePubsubTable; +}; + +#define NOTIFY 0 +#define CLEAR_ALARM 1 +#define RAISE_ALARM 2 +#define ACK_ALARM 3 +#define UNACK_ALARM 4 + +#define LOG_EVENT(name, source, action, MSG, ...) alertmgr::EventNotify::getInstance()->_send_event(#name, source, action, MSG, ##__VA_ARGS__) +} + +#endif /* SWSS_COMMON_EVENTNOTIFY_H */ diff --git a/src/sonic-eventd/lib/src/Makefile.am b/src/sonic-eventd/lib/src/Makefile.am new file mode 100644 index 000000000000..84dbe3114fe9 --- /dev/null +++ b/src/sonic-eventd/lib/src/Makefile.am @@ -0,0 +1,15 @@ + +INCLUDES = -I ../include + +lib_LTLIBRARIES = libeventnotify.la + +if DEBUG +DBGFLAGS = -ggdb -DDEBUG +else +DBGFLAGS = -ggdb -DDEBUG +endif + +libeventnotify_la_SOURCES = eventnotify.cpp + +libeventnotify_la_CPPFLAGS = $(DBGFLAGS) $(AM_CFLAGS) $(CFLAGS_COMMON) $(COV_CFLAGS) $(ASAN_CFLAGS) +libeventnotify_la_LIBADD = -lhiredis -lswsscommon $(COV_LDFLAGS) $(ASAN_LDFLAGS) diff --git a/src/sonic-eventd/lib/src/eventnotify.cpp b/src/sonic-eventd/lib/src/eventnotify.cpp new file mode 100644 index 000000000000..bfa0af570718 --- /dev/null +++ b/src/sonic-eventd/lib/src/eventnotify.cpp @@ -0,0 +1,96 @@ +#include "eventnotify.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "schema.h" +#include "select.h" +#include "dbconnector.h" +#include "redisclient.h" +#include "timestamp.h" + +using namespace swss; +using namespace std; +using namespace std::chrono; + +namespace alertmgr { + +string getActionStr(int action); +string getTimestamp(); +EventNotify *EventNotify::m_pInstance = NULL; +uint64_t seq_id = 1; + +EventNotify::EventNotify(DBConnector* stateDbConnector): + m_statePubsubTable(stateDbConnector, EVENT_PUBSUB_TABLE_NAME) { + SWSS_LOG_ENTER(); +} + +EventNotify::~EventNotify() { +} + +EventNotify *EventNotify::getInstance() +{ + if (!m_pInstance) { + DBConnector db("EVENT_DB", 0); + m_pInstance = new EventNotify(&db); + } + + return m_pInstance; +} + +// timeticks are relative to the Unix Epoch +string getTimestamp() { + const auto p1 = system_clock::now(); + return to_string(duration_cast(p1.time_since_epoch()).count()); +} + +string getActionStr(int action) { + switch (action) { + case CLEAR_ALARM: + return string("CLEAR"); + case RAISE_ALARM: + return string("RAISE"); + case ACK_ALARM: + return string("ACKNOWLEDGE"); + case UNACK_ALARM: + return string("UNACKNOWLEDGE"); + default: + return string(); + } +} + +void EventNotify::_send_event(const char *ev_name_p, const char *source_p, int action, const char *fmt, ...) +{ + va_list args; + char buff[1024]; + memset(buff, 0, sizeof(buff)); + + SWSS_LOG_ENTER(); + + va_start(args, fmt); + vsnprintf(buff, sizeof(buff), fmt, args); + va_end (args); + + string tblkey = string(ev_name_p) + to_string(seq_id++); + vector fvs; + fvs.emplace_back("type-id", string(ev_name_p)); + fvs.emplace_back("text", buff); + fvs.emplace_back("action", getActionStr(action)); + if (source_p && (strlen(source_p) > 0)) { + fvs.emplace_back("resource", string(source_p)); + } + fvs.emplace_back("time-created", getTimestamp()); + + m_statePubsubTable.set(tblkey, fvs); +} + +}; diff --git a/src/sonic-eventd/src/Makefile.am b/src/sonic-eventd/src/Makefile.am new file mode 100644 index 000000000000..8826afcb1612 --- /dev/null +++ b/src/sonic-eventd/src/Makefile.am @@ -0,0 +1,25 @@ +INCLUDES = -I ../include + +bin_PROGRAMS = eventd + +lib_LTLIBRARIES = libloghandler.la + +if DEBUG +DBGFLAGS = -ggdb -DDEBUG +else +#DBGFLAGS = -g -DNDEBUG +DBGFLAGS = -ggdb -DDEBUG +endif + +LIB_LOG_HANDLER=libloghandler.la + +libloghandler_la_SOURCES = loghandler.cpp +libloghandler_la_LDFLAGS = -version-info 0:0:0 + +eventd_SOURCES = eventd.cpp eventutils.cpp eventconsume.cpp + +#CXXFLAGS += -ffunction-sections -fdata-sections +#eventd_CPPFLAGS = $(DBGFLAGS) $(AM_CFLAGS) $(CFLAGS_COMMON) $(COV_CFLAGS) $(ASAN_CFLAGS) $(CXXFLAGS) +eventd_CPPFLAGS = $(DBGFLAGS) $(AM_CFLAGS) $(CFLAGS_COMMON) $(COV_CFLAGS) $(ASAN_CFLAGS) +eventd_LDADD = -lnl-3 -lhiredis -lswsscommon -leventnotify $(LIB_LOG_HANDLER) $(COV_LDFLAGS) $(ASAN_LDFLAGS) + diff --git a/src/sonic-eventd/src/eventconsume.cpp b/src/sonic-eventd/src/eventconsume.cpp new file mode 100644 index 000000000000..1bfb9c8d1a31 --- /dev/null +++ b/src/sonic-eventd/src/eventconsume.cpp @@ -0,0 +1,785 @@ +#include "eventconsume.h" +#include "eventnotify.h" +#include "dbconnector.h" +#include "subscriberstatetable.h" +#include "select.h" +#include "loghandler.h" +#include "eventutils.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace evt { + +using namespace swss; +using namespace std; +using namespace evtutils; +using namespace std::chrono; + +constexpr size_t LOG_SZ = 500; +constexpr char LOG_TYPE_EVENT[] = "event"; +constexpr char LOG_TYPE_ALARM[] = "alarm"; + +// map to store sequence-id for alarms +unordered_map cal_lookup_map; + +// Map to hold "default" map of events +EventMap default_static_event_table; + +// temporary map to hold merge of default map of events and any event profile +EventMap static_event_table; + +volatile bool g_run = true; +uint64_t seq_id = 0; +uint64_t PURGE_SECONDS = 86400; + +//typedef pair pi; +typedef pair pi; +priority_queue, greater > event_history_list; + +map SYSLOG_SEVERITY = { + {EVENT_SEVERITY_CRITICAL_STR, LOG_ALERT}, + {EVENT_SEVERITY_MAJOR_STR, LOG_CRIT}, + {EVENT_SEVERITY_MINOR_STR, LOG_ERR}, + {EVENT_SEVERITY_WARNING_STR, LOG_WARNING}, + {EVENT_SEVERITY_INFORMATIONAL_STR, LOG_NOTICE} +}; + +map SYSLOG_SEVERITY_STR = { + {LOG_ALERT , EVENT_SEVERITY_CRITICAL_STR}, + {LOG_CRIT , EVENT_SEVERITY_MAJOR_STR}, + {LOG_ERR , EVENT_SEVERITY_MINOR_STR}, + {LOG_WARNING , EVENT_SEVERITY_WARNING_STR}, + {LOG_NOTICE , EVENT_SEVERITY_INFORMATIONAL_STR} +}; + +static string flood_ev_id; +static string flood_ev_action; +static string flood_ev_resource; +static string flood_ev_msg; + +EventConsume::EventConsume(DBConnector* dbConn) : + m_eventTable(dbConn, EVENT_HISTORY_TABLE_NAME), + m_alarmTable(dbConn, EVENT_CURRENT_ALARM_TABLE_NAME), + m_eventStatsTable(dbConn, EVENT_STATS_TABLE_NAME), + m_alarmStatsTable(dbConn, EVENT_ALARM_STATS_TABLE_NAME), + m_consumerTable(dbConn, EVENT_PUBSUB_TABLE_NAME), + m_evprofileTable(dbConn, EVENT_EVPROFILE_TABLE_NAME), + m_eventPubSubTable(dbConn, EVENT_PUBSUB_TABLE_NAME) { + SWSS_LOG_ENTER(); + + // open syslog connection + openSyslog(); + + // init stats + initStats(); + + // populate local queue of event history table + read_events(); + + // read and apply eventd configuration files + // read eventd.json and apply it on history table. + // read default and custom profiles, build static_event_table + read_eventd_config(); + + // if symlink pointing to default/custom profile, doesnt exist, create one + create_symlink(); + + SWSS_LOG_NOTICE("DONE WITH EventConsume constructor"); +} + +EventConsume::~EventConsume() { +} + +uint64_t EventConsume::get_seq_id() { + return seq_id; +} + +void EventConsume::run() +{ + SWSS_LOG_ENTER(); + + swss::Select s; + KeyOpFieldsValuesTuple kco; + + // selectable for various tables + s.addSelectable(&m_consumerTable); + s.addSelectable(&m_evprofileTable); + + while (g_run) { + swss::Selectable *sel; + + std::deque kco; + + int result = s.select(&sel); + + if (result == Select::OBJECT) { + if (sel == &m_evprofileTable) { + m_evprofileTable.pops(kco); + handle_custom_evprofile(kco); + } else if (sel == &m_consumerTable) { + m_consumerTable.pops(kco); + handle_notification(kco); + } else { + SWSS_LOG_DEBUG("received invalid trigger by select"); + } + } + } +} + +void EventConsume::read_eventd_config(bool read_all) { + // read manifest file for config options + if (read_all) { + read_config_and_purge(); + } + + // read from default map + static_event_table.clear(); + if (!parse(EVENTD_DEFAULT_MAP_FILE, static_event_table)) { + SWSS_LOG_ERROR("Can not initialize event map"); + closeSyslog(); + exit(0); + } + + // Maintain the native default profile in default_static_event_table. + if (default_static_event_table.empty()) { + default_static_event_table = static_event_table; + } + + // read from any potential event profile + EventMap profile_map; + profile_map.clear(); + if (parse(EVENTD_PROFILE_SYMLINK, profile_map)) { + // merge both default map and profile map + merge(static_event_table, profile_map); + } + + SWSS_LOG_NOTICE("Event map is built as follows:"); + for (auto& x: static_event_table) { + SWSS_LOG_NOTICE(" %s (%s %s %s)", x.first.c_str(), x.second.severity.c_str(), x.second.enable.c_str(), x.second.static_event_msg.c_str()); + } +} + +void EventConsume::handle_notification(std::deque kco) +{ + SWSS_LOG_ENTER(); + + for (auto entry: kco) { + string ev_id, ev_msg, ev_src, ev_act, ev_timestamp, ev_type("EVENT"), ev_static_msg(""), ev_reckey; + string ev_sev = string(EVENT_SEVERITY_INFORMATIONAL_STR); + bool is_raise = false; + bool is_clear = false; + bool is_ack = false; + vector vec; + + ev_reckey = kfvKey(entry); + std::string op = kfvOp(entry); + if (op != "SET") { + continue; + } + + // parse the record and fetch various event fields + const vector& data = kfvFieldsValues(entry); + fetchFieldValues(data, vec, ev_id, ev_msg, ev_src, ev_act, ev_timestamp); + + // delete the pubsub entry + m_eventPubSubTable.del(ev_reckey); + + // flood protection. If a rogue application sends same event repeatedly, throttle repeated instances of that event + if (isFloodedEvent(ev_src, ev_act, ev_id, ev_msg)) { + continue; + } + + // get static info + if (!staticInfoExists(ev_id, ev_act, ev_sev, ev_static_msg, vec)) { + continue; + } + + // increment save seq-id for the newly received event + seq_id++; + FieldValueTuple seqfv("id", to_string(seq_id)); + vec.push_back(seqfv); + + if (ev_act.length() > 0) { + SWSS_LOG_DEBUG("ev_act %s", ev_act.c_str()); + ev_type = "ALARM"; + string almkey = ev_id; + if (!ev_src.empty()) { + almkey += "|" + ev_src; + } + + if (ev_act.compare(EVENT_ACTION_RAISE_STR) == 0) { + is_raise = true; + // add entry to the lookup map + cal_lookup_map.insert(make_pair(almkey, seq_id)); + + // add acknowledged field intializing it to false + FieldValueTuple seqfv1("acknowledged", "false"); + vec.push_back(seqfv1); + m_alarmTable.set(to_string(seq_id), vec); + + // update alarm counters + updateAlarmStatistics(ev_sev, ev_act); + } else if (ev_act.compare(EVENT_ACTION_CLEAR_STR) == 0) { + is_clear = true; + SWSS_LOG_DEBUG(" Received clear alarm for %s", almkey.c_str()); + + bool ack_flag = false; + // remove entry from local cache, alarm table + if (! udpateLocalCacheAndAlarmTable(almkey, ack_flag)) { + continue; + } + + // update alarm counters ONLY if it has not been ack'd before. + // This is because when alarm is ack'd, alarms/severity counter is reduced already. + if (!ack_flag) { + updateAlarmStatistics(ev_sev, ev_act); + } else { + // if it has been ack'd before, ack counter would have been incremented for this alrm. + // Now is the time reduce it. + clearAckAlarmStatistic(); + } + } else { + // ack/unack events comes with seq-id of raised alarm as resource field. + // fetch details of "raised" alarm record + string raise_act; + string raise_ack_flag; + string raise_ts; + + // fetch information from raised event record + fetchRaiseInfo(vec, ev_src, ev_id, ev_sev, raise_act, raise_ack_flag, raise_ts); + + if (ev_act.compare(EVENT_ACTION_ACK_STR) == 0) { + if (raise_ack_flag.compare("true") == 0) { + SWSS_LOG_NOTICE(" %s/%s is already acknowledged", ev_id.c_str(), ev_src.c_str()); + continue; + } + if (raise_act.compare(EVENT_ACTION_RAISE_STR) == 0) { + is_ack = true; + SWSS_LOG_DEBUG(" received ACKnowledge event - %s/%s", ev_id.c_str(), ev_src.c_str()); + + // update the record with ack flag and ack-time and stats + updateAckInfo(is_ack, ev_timestamp, ev_sev, ev_act, ev_src); + } else { + SWSS_LOG_NOTICE(" %s/%s is not in raised state", ev_id.c_str(), ev_src.c_str()); + continue; + } + } else if (ev_act.compare(EVENT_ACTION_UNACK_STR) == 0) { + if (raise_ack_flag.compare("true") == 0) { + SWSS_LOG_DEBUG(" received un-ACKnowledge event - %s/%s", ev_id.c_str(), ev_src.c_str()); + + // update the record with ack flag and ack-time and stats + updateAckInfo(is_ack, ev_timestamp, ev_sev, ev_act, ev_src); + } else { + SWSS_LOG_NOTICE(" %s/%s is already un-acknowledged", ev_id.c_str(), ev_src.c_str()); + continue; + } + } + } + } + // verify the size of history table; delete older entry; add new entry + update_events(to_string(seq_id), ev_timestamp, vec); + + updateEventStatistics(true, is_raise, is_ack, is_clear); + + // raise a syslog message + writeToSyslog(ev_id, (int) (SYSLOG_SEVERITY.find(ev_sev)->second), ev_type, ev_act, ev_msg, ev_static_msg); + } + + return; +} + +void EventConsume::read_events() { + vector tuples; + m_eventTable.getContent(tuples); + + SWSS_LOG_ENTER(); + // find out last sequence-id; build local history list + for (auto tuple: tuples) { + for (auto fv: kfvFieldsValues(tuple)) { + if (fvField(fv) == "time-created") { + char* end; + uint64_t seq = strtoull(kfvKey(tuple).c_str(), &end,10); + if (seq > seq_id) { + seq_id = seq; + } + uint64_t val = strtoull(fvValue(fv).c_str(), &end,10); + event_history_list.push(make_pair( seq, val )); + } + } + } + SWSS_LOG_NOTICE("eventd sequence-id intialized to %lu", seq_id); +} + +void EventConsume::updateAlarmStatistics(string ev_sev, string ev_act) { + vector vec; + vector temp; + + // severity counter names are of lower case + transform(ev_sev.begin(), ev_sev.end(), ev_sev.begin(), ::tolower); + + if (m_alarmStatsTable.get("state", vec)) { + for (auto fv: vec) { + if (!fv.first.compare("alarms")) { + if ((ev_act.compare(EVENT_ACTION_RAISE_STR) == 0) || (ev_act.compare(EVENT_ACTION_UNACK_STR) == 0)) { + fv.second = to_string(stoi(fv.second.c_str())+1); + } else { + fv.second = to_string(stoi(fv.second.c_str())-1); + } + temp.push_back(fv); + } else if (!fv.first.compare(ev_sev)) { + if ((ev_act.compare(EVENT_ACTION_RAISE_STR) == 0) || (ev_act.compare(EVENT_ACTION_UNACK_STR) == 0)) { + fv.second = to_string(stoi(fv.second.c_str())+1); + } else { + fv.second = to_string(stoi(fv.second.c_str())-1); + } + temp.push_back(fv); + } else if (!fv.first.compare("acknowledged")) { + if (ev_act.compare(EVENT_ACTION_ACK_STR) == 0) { + fv.second = to_string(stoi(fv.second.c_str())+1); + } else if (ev_act.compare(EVENT_ACTION_UNACK_STR) == 0) { + fv.second = to_string(stoi(fv.second.c_str())-1); + } + temp.push_back(fv); + } + } + m_alarmStatsTable.set("state", temp); + } else { + SWSS_LOG_ERROR("Can not update alarm statistics (table does not exist)"); + } +} + +void EventConsume::updateEventStatistics(bool is_add, bool is_raise, bool is_ack, bool is_clear) { + vector vec; + vector temp; + + if (m_eventStatsTable.get("state", vec)) { + for (auto fv: vec) { + if (!fv.first.compare("events")) { + if (is_add) { + fv.second = to_string(stoi(fv.second.c_str())+1); + } else { + fv.second = to_string(stoi(fv.second.c_str())-1); + } + temp.push_back(fv); + } else if (!fv.first.compare("raised")) { + if (is_raise) { + if (is_add) { + fv.second = to_string(stoi(fv.second.c_str())+1); + } else { + fv.second = to_string(stoi(fv.second.c_str())-1); + } + temp.push_back(fv); + } + } else if (!fv.first.compare("cleared")) { + if (is_clear) { + if (is_add) { + fv.second = to_string(stoi(fv.second.c_str())+1); + } else { + fv.second = to_string(stoi(fv.second.c_str())-1); + } + temp.push_back(fv); + } + } else if (!fv.first.compare("acked")) { + if (is_ack) { + if (is_add) { + fv.second = to_string(stoi(fv.second.c_str())+1); + } else { + fv.second = to_string(stoi(fv.second.c_str())-1); + } + temp.push_back(fv); + } + } + } + + m_eventStatsTable.set("state", temp); + } else { + SWSS_LOG_ERROR("Can not update event statistics (table does not exist)"); + } +} + +void EventConsume::modifyEventStats(string seq_id) { + vector rec; + m_eventTable.get(seq_id, rec); + bool is_raise = false; + bool is_clear = false; + bool is_ack = false; + for (auto fvr: rec) { + if (!fvr.first.compare("action")) { + if (!fvr.second.compare(EVENT_ACTION_RAISE_STR)) { + is_raise = true; + } else if (!fvr.second.compare(EVENT_ACTION_CLEAR_STR)) { + is_clear = true; + } + } + if (!fvr.first.compare("acknowledged")) { + if (!fvr.second.compare("true")) { + is_ack = true; + } + } + } + updateEventStatistics(false, is_raise, is_ack, is_clear); +} + +void EventConsume::purge_events() { + SWSS_LOG_ENTER(); + uint32_t size = event_history_list.size(); + + while (size >= count) { + pair oldest_entry = event_history_list.top(); + SWSS_LOG_NOTICE("Rollover based on count(%d/%d). Deleting %lu", size, count, oldest_entry.first); + m_eventTable.del(to_string(oldest_entry.first)); + modifyEventStats(to_string(oldest_entry.first)); + event_history_list.pop(); + --size; + } + + const auto p1 = system_clock::now(); + unsigned tnow_seconds = duration_cast(p1.time_since_epoch()).count(); + + while (!event_history_list.empty()) { + pair oldest_entry = event_history_list.top(); + unsigned old_seconds = oldest_entry.second / 1000000000ULL; + + if ((tnow_seconds - old_seconds) > PURGE_SECONDS) { + SWSS_LOG_NOTICE("Rollover based on time (%lu days). Deleting %lu.. now %u old %u", (PURGE_SECONDS/days), oldest_entry.first, tnow_seconds, old_seconds); + m_eventTable.del(to_string(oldest_entry.first)); + modifyEventStats(to_string(oldest_entry.first)); + event_history_list.pop(); + } else { + return; + } + } + return; +} + +void EventConsume::read_config_and_purge() { + days = 0; + count = 0; + // read from the manifest file + parse_config(EVENTD_CONF_FILE, days, count); + SWSS_LOG_NOTICE("max-days %d max-records %d", days, count); + + // update the nanosecond limit + PURGE_SECONDS *= days; + + // purge events based on # of days + purge_events(); +} + +void EventConsume::update_events(string seq_id, string ts, vector vec) { + // purge events based on # of days + purge_events(); + + // now add the event to the event table + m_eventTable.set(seq_id, vec); + + // store it into the event history list + char* end; + uint64_t seq = strtoull(seq_id.c_str(), &end, 10); + uint64_t val = strtoull(ts.c_str(), &end, 10); + event_history_list.push(make_pair( seq, val )); +} + +void EventConsume::handle_custom_evprofile(std::deque entries) { + string filename; + for (auto entry: entries) { + std::string op = kfvOp(entry); + if (op != "SET") return; + const vector& data = kfvFieldsValues(entry); + for (auto idx : data) { + if (fvField(idx) == "name") { + filename = fvValue(idx); + break; + } + } + } + + if (filename.empty()) { + SWSS_LOG_ERROR("Received Event Profile name is empty."); + return; + } + string custom_profile = EVENTD_PROFILE_DIR + filename; + + SWSS_LOG_DEBUG("Received profile is %s", custom_profile.c_str()); + + // make sure that event profile is not already configured + char buf[1024]; + std::size_t len; + if ((len = readlink(EVENTD_PROFILE_SYMLINK, buf, sizeof(buf)-1)) > 0) { + buf[len] = '\0'; + + if (string(buf).compare(custom_profile) == 0) { + SWSS_LOG_DEBUG("Event Profile name is already configured."); + return; + } + } + + // the profile is already validated by rest-server. create symlink + if (unlink(EVENTD_PROFILE_SYMLINK) != 0) { + // it is possible.. if there is no symlink exists and user trying for the first time + SWSS_LOG_DEBUG("Unlink of custom profile failed"); + } + if (symlink(custom_profile.c_str(), EVENTD_PROFILE_SYMLINK) != 0) { + SWSS_LOG_ERROR("Error applying custom profile"); + } else { + SWSS_LOG_DEBUG("Applying custom profile"); + read_eventd_config(false); + // generate an event informing new profile is in effect + LOG_EVENT(CUSTOM_EVPROFILE_CHANGE, custom_profile.c_str(), NOTIFY, "Custom Event Profile %s is applied.", filename.c_str()); + + // walk through event table and remove those alarms for which enable flag is set to false + for (auto it : cal_lookup_map) { + //key in the cal_lookup_map is tokenized with | with event-id and source + char *evid = strtok((char *) (it.first.c_str()), "|"); + // find the record in the static_event_table that merged the custom profile + auto itr = static_event_table.find(string(evid)); + if (itr != static_event_table.end()) { + EventInfo tmp = (EventInfo) (itr->second); + // if enable flag is false, remove the record from alarm table + if (tmp.enable == EVENT_ENABLE_FALSE_STR) { + SWSS_LOG_NOTICE("enable for evid %s is false.. clearing alarm", evid); + uint64_t lookup_seq_id = (long unsigned int) (it.second); + cal_lookup_map.erase(it.first); + m_alarmTable.del(to_string(lookup_seq_id)); + // update alarm stats + updateAlarmStatistics(tmp.severity, EVENT_ACTION_CLEAR_STR); + } + } + } + } +} + +void EventConsume::resetAlarmStats(int alarms, int critical, int major, int minor, int warning, int acknowledged) { + vector temp; + FieldValueTuple fv; + map::iterator it; + for (it = SYSLOG_SEVERITY_STR.begin(); it != SYSLOG_SEVERITY_STR.end(); it++) { + // there wont be any informational alarms + if (it->second.compare(EVENT_SEVERITY_CRITICAL_STR) == 0) { + fv = FieldValueTuple("critical", to_string(critical)); + temp.push_back(fv); + } else if (it->second.compare(EVENT_SEVERITY_MAJOR_STR) == 0) { + fv = FieldValueTuple("major", to_string(major)); + temp.push_back(fv); + } else if (it->second.compare(EVENT_SEVERITY_MINOR_STR) == 0) { + fv = FieldValueTuple("minor", to_string(minor)); + temp.push_back(fv); + } else if (it->second.compare(EVENT_SEVERITY_WARNING_STR) == 0) { + fv = FieldValueTuple("warning", to_string(warning)); + temp.push_back(fv); + } + } + fv = FieldValueTuple("alarms", to_string(alarms)); + temp.push_back(fv); + fv = FieldValueTuple("acknowledged", to_string(acknowledged)); + temp.push_back(fv); + m_alarmStatsTable.set("state", temp); +} + +void EventConsume::clearAckAlarmStatistic() { + vector vec; + vector temp; + + if (m_alarmStatsTable.get("state", vec)) { + for (auto fv: vec) { + if (!fv.first.compare("acknowledged")) { + fv.second = to_string(stoi(fv.second.c_str())-1); + temp.push_back(fv); + m_alarmStatsTable.set("state", temp); + return; + } + } + } +} + + +void EventConsume::fetchFieldValues(const vector& data, + vector &vec, + string &ev_id, + string &ev_msg, + string &ev_src, + string &ev_act, + string &ev_timestamp) { + for (auto idx : data) { + if (fvField(idx) == "type-id") { + ev_id = fvValue(idx); + vec.push_back(idx); + SWSS_LOG_DEBUG("type-id: <%s> ", ev_id.c_str()); + } else if (fvField(idx) == "text") { + ev_msg = fvValue(idx); + vec.push_back(idx); + SWSS_LOG_DEBUG("text: <%s> ", ev_msg.c_str()); + } else if (fvField(idx) == "resource") { + ev_src = fvValue(idx); + vec.push_back(idx); + SWSS_LOG_DEBUG("resource: <%s> ", ev_src.c_str()); + } else if (fvField(idx) == "action") { + ev_act = fvValue(idx); + // for events, action is empty + if (!ev_act.empty()) { + vec.push_back(idx); + } + } else if (fvField(idx) == "time-created") { + ev_timestamp = fvValue(idx); + vec.push_back(idx); + SWSS_LOG_DEBUG("time-created: <%s> ", ev_timestamp.c_str()); + } + } +} + +bool EventConsume::isFloodedEvent(string ev_src, string ev_act, string ev_id, string ev_msg) { + // flood protection. If a rogue application sends same event repeatedly, throttle repeated instances of that event + if (!flood_ev_resource.compare(ev_src) && + !flood_ev_action.compare(ev_act) && + !flood_ev_id.compare(ev_id) && + !(flood_ev_msg.compare(ev_msg))) { + SWSS_LOG_INFO("Ignoring the event %s from %s action %s msg %s as it is repeated", ev_id.c_str(), ev_src.c_str(), ev_act.c_str(), ev_msg.c_str()); + return true; + } + + flood_ev_resource = ev_src; + flood_ev_action = ev_act; + flood_ev_id = ev_id; + flood_ev_msg = ev_msg; + return false; +} + +bool EventConsume::staticInfoExists(string &ev_id, string &ev_act, string &ev_sev, string &ev_static_msg, vector &vec) { + auto it = static_event_table.find(ev_id); + if (it != static_event_table.end()) { + EventInfo tmp = (EventInfo) (it->second); + // discard the event as event_static_map shows enable is false for this event + if (tmp.enable == EVENT_ENABLE_FALSE_STR) { + SWSS_LOG_INFO("Discarding event <%s> as it is set to disabled", ev_id.c_str()); + return false;; + } + + // get severity in the map and store it in the db + ev_sev = tmp.severity; + ev_static_msg = tmp.static_event_msg; + SWSS_LOG_DEBUG("static info: <%s> <%s> ", tmp.severity.c_str(), tmp.static_event_msg.c_str()); + + FieldValueTuple seqfv1("severity", tmp.severity); + vec.push_back(seqfv1); + return true; + } else { + // dont process the incoming alarms if action is neither raise nor clear + // for ack/unack, no need for this check + if ((ev_act.compare(EVENT_ACTION_ACK_STR) && ev_act.compare(EVENT_ACTION_UNACK_STR))) { + SWSS_LOG_ERROR("static info NOT FOUND for <%s> ", ev_id.c_str()); + return false; + } + } + return true; +} + +bool EventConsume::udpateLocalCacheAndAlarmTable(string almkey, bool &ack_flag) { + // find and remove the raised alarm + uint64_t lookup_seq_id = 0; + auto it = cal_lookup_map.find(almkey); + if (it != cal_lookup_map.end()) { + lookup_seq_id = (uint64_t) (it->second); + cal_lookup_map.erase(almkey); + + // get status of is_aknowledged flag so that we dont decrement counters twice + vector alm_rec; + m_alarmTable.get(to_string(lookup_seq_id), alm_rec); + for (auto fvr: alm_rec) { + if (!fvr.first.compare("acknowledged")) { + ack_flag = (fvr.second.compare("true") == 0) ? true : false; + break; + } + } + + // delete the record from alarm table + m_alarmTable.del(to_string(lookup_seq_id)); + } else { + // possible - when event profile removes alarms for which enable is false and application cleared them later. + // ignore by logging a debug message.. + SWSS_LOG_INFO("Received alarm-clear for non-existing alarm %s", almkey.c_str()); + return false; + } + return true; +} + +void EventConsume::initStats() { + vector vec; + // possible after a cold-boot or very first time + if (! m_eventStatsTable.get("state", vec)) { + FieldValueTuple fv; + vector temp; + + SWSS_LOG_DEBUG("resetting Event Statistics table"); + fv = FieldValueTuple("events", to_string(0)); + temp.push_back(fv); + fv = FieldValueTuple("raised", to_string(0)); + temp.push_back(fv); + fv = FieldValueTuple("cleared", to_string(0)); + temp.push_back(fv); + fv = FieldValueTuple("acked", to_string(0)); + temp.push_back(fv); + m_eventStatsTable.set("state", temp); + } + if (! m_alarmStatsTable.get("state", vec)) { + SWSS_LOG_DEBUG("resetting Alarm Statistics table"); + resetAlarmStats(0, 0, 0, 0, 0, 0); + } +} + +void EventConsume::updateAckInfo(bool is_ack, string ev_timestamp, string ev_sev, string ev_act, string ev_src) { + vector ack_vec; + + FieldValueTuple seqfv1("acknowledged", (is_ack ? "true" : "false")); + ack_vec.push_back(seqfv1); + + FieldValueTuple seqfv2("acknowledge-time", ev_timestamp); + ack_vec.push_back(seqfv2); + + // update alarm stats + updateAlarmStatistics(ev_sev, ev_act); + + // update alarm/event tables for the "raise" record with ack flag and ack timestamp + // for ack/unack, ev_src contains the "seq-id" + m_alarmTable.set(ev_src, ack_vec); + m_eventTable.set(ev_src, ack_vec); +} + + +void EventConsume::fetchRaiseInfo(vector &vec, string ev_src, string &ev_id, string &ev_sev, string &raise_act, + string &raise_ack_flag, string &raise_ts) { + vector raise_vec; + m_alarmTable.get(ev_src, raise_vec); + for (auto fv: raise_vec) { + if (!fv.first.compare("type-id")) { + ev_id = fv.second; + vec.push_back(fv); + } + if (!fv.first.compare("severity")) { + ev_sev = fv.second; + vec.push_back(fv); + } + if (!fv.first.compare("action")) { + raise_act = fv.second; + } + if (!fv.first.compare("acknowledged")) { + raise_ack_flag = fv.second; + } + if (!fv.first.compare("time-created")) { + raise_ts = fv.second; + } + } +} + +}; diff --git a/src/sonic-eventd/src/eventd.cpp b/src/sonic-eventd/src/eventd.cpp new file mode 100644 index 000000000000..0ff0373c8dd4 --- /dev/null +++ b/src/sonic-eventd/src/eventd.cpp @@ -0,0 +1,29 @@ +#include "eventconsume.h" +#include + +static evt::EventConsume *evtd_instance = NULL; + +void signalHandler(const int signal) { + SWSS_LOG_NOTICE("in signalHandler"); + + if (signal == SIGINT) { + evtd_instance->read_eventd_config(); + } +} + +int main() +{ + swss::Logger::getInstance().setMinPrio(swss::Logger::SWSS_DEBUG); + + swss::DBConnector eventDb("EVENT_DB", 0); + + // register signal SIGINT and signal handler + signal(SIGINT, signalHandler); + + evt::EventConsume evtd(&eventDb); + evtd_instance = &evtd; + + evtd.run(); + + return 0; +} diff --git a/src/sonic-eventd/src/eventutils.cpp b/src/sonic-eventd/src/eventutils.cpp new file mode 100644 index 000000000000..0ab74e39278e --- /dev/null +++ b/src/sonic-eventd/src/eventutils.cpp @@ -0,0 +1,112 @@ +#include "eventutils.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "json.h" +#include "json.hpp" + +namespace evtutils { +using namespace std; +using namespace swss; +using namespace std::chrono; +using json = nlohmann::json; + +bool isValidSeverity(string severityStr) { + transform(severityStr.begin(), severityStr.end(), severityStr.begin(), ::toupper); + if (severityStr == EVENT_SEVERITY_MAJOR_STR) return true; + if (severityStr == EVENT_SEVERITY_CRITICAL_STR) return true; + if (severityStr == EVENT_SEVERITY_MINOR_STR) return true; + if (severityStr == EVENT_SEVERITY_WARNING_STR) return true; + if (severityStr == EVENT_SEVERITY_INFORMATIONAL_STR) return true; + return false; +} + +bool isValidEnable(string enableStr) { + if (enableStr == EVENT_ENABLE_TRUE_STR) return true; + if (enableStr == EVENT_ENABLE_FALSE_STR) return true; + return false; +} + +bool parse_config(const char *filename, unsigned int& days, unsigned int& count) { + days = EHT_MAX_DAYS; + count = EHT_MAX_ELEMS; + std::ifstream ifs(filename); + json j = json::parse(ifs); + for (json::iterator it = j.begin(); it != j.end(); ++it) { + if(it.key() == "max-days") { + days = it.value(); + } + if(it.key() == "max-records") { + count = it.value(); + } + } + return true; +} + +bool parse(const char *filename, EventMap& tmp_event_table) { + ifstream fs(filename); + if (!fs.is_open()) { + return false; + } + + fstream file(filename, fstream::in); + json j; + file >> j; + + if (j["events"].size() == 0) { + SWSS_LOG_ERROR("No entries in 'events' field in %s", filename); + return false; + } + + for (size_t i = 0; i < j["events"].size(); i++) { + auto elem = j["events"][i]; + struct EventInfo_t ev_info; + string ev_name = elem["name"]; + ev_info.severity = elem["severity"]; + ev_info.enable = elem["enable"]; + ev_info.static_event_msg = elem["message"]; + tmp_event_table.emplace(ev_name, ev_info); + } + + return true; +} + +void merge(EventMap& static_event_table, EventMap &profile_map) { + for (auto it = profile_map.begin(); it != profile_map.end(); it++) { + if (static_event_table.find(it->first) != static_event_table.end()) { + //TODO: try copying individual parametrs + static_event_table[it->first] = it->second; + } else { + static_event_table.emplace(it->first, it->second); + } + } +} + +void create_symlink() { + struct stat info; + if (lstat(EVENTD_PROFILE_SYMLINK, &info) != 0) { + if (symlink(EVENTD_DEFAULT_MAP_FILE, EVENTD_PROFILE_SYMLINK) != 0) { + SWSS_LOG_ERROR("Error creating symlink to default profile"); + } else { + SWSS_LOG_NOTICE("Created symlink to default profile"); + } + } +} + +// timeticks are relative to the Unix Epoch +string getTimeTicks() { + const auto p1 = system_clock::now(); + return to_string(duration_cast(p1.time_since_epoch()).count()); +} + +} + diff --git a/src/sonic-eventd/src/loghandler.cpp b/src/sonic-eventd/src/loghandler.cpp new file mode 100755 index 000000000000..d560ccc2e0e3 --- /dev/null +++ b/src/sonic-eventd/src/loghandler.cpp @@ -0,0 +1,37 @@ +#include +#include + +extern "C" void openSyslog() { + openlog (NULL, LOG_CONS | LOG_PID | LOG_NDELAY, LOG_LOCAL4); +} + +extern "C" void writeToSyslog(std::string ev_id, int ev_sev, std::string ev_type, std::string ev_act, std::string ev_msg, std::string ev_static_msg) { + int SYSLOG_FACILITY = LOG_LOCAL4; + if (ev_act.empty()) { + const char LOG_FORMAT[] = "[%s], %%%s: %s %s"; + // event Type + // Event Name + // Static Desc + // Dynamic Desc + + // raise a syslog message + syslog(LOG_MAKEPRI(ev_sev, SYSLOG_FACILITY), LOG_FORMAT, + ev_type.c_str(), + ev_id.c_str(), ev_static_msg.c_str(), ev_msg.c_str()); + } else { + const char LOG_FORMAT[] = "[%s] (%s), %%%s: %s %s"; + // event Type + // event action + // Event Name + // Static Desc + // Dynamic Desc + // raise a syslog message + syslog(LOG_MAKEPRI(ev_sev, SYSLOG_FACILITY), LOG_FORMAT, + ev_type.c_str(), ev_act.c_str(), + ev_id.c_str(), ev_static_msg.c_str(), ev_msg.c_str()); + } +} + +extern "C" void closeSyslog() { + closelog (); +} diff --git a/src/sonic-eventd/tests/event_unittest.py b/src/sonic-eventd/tests/event_unittest.py new file mode 100644 index 000000000000..7e167e96a41c --- /dev/null +++ b/src/sonic-eventd/tests/event_unittest.py @@ -0,0 +1,253 @@ +from eventnotify import eventnotify +from swsscommon import swsscommon +import time +import enum +import time +import os +import json + +class EventUnitTest: + def unittest_init(self): + self.event_db = swsscommon.DBConnector("EVENT_DB", 0, True) + self.eventTbl = swsscommon.Table(self.event_db, "EVENT") + self.alarmTbl = swsscommon.Table(self.event_db, "ALARM") + self.eventStateTbl = swsscommon.Table(self.event_db, "EVENT_STATS") + self.alarmStateTbl = swsscommon.Table(self.event_db, "ALARM_STATS") + self.evProfileTbl = swsscommon.Table(self.event_db, "EVPROFILE") + self.notify = eventnotify.EventNotify() + + def test_event(self): + print("\nTEST: raise event") + eventCount = 0 + newEventCount = 0 + fvs = 0 + (status, fvs) = self.eventStateTbl.get("state") + for fv in fvs: + if fv[0] == "events": + eventCount = int(fv[1]) + + self.notify.logevent("CUSTOM_EVPROFILE_CHANGE", "x.json", eventnotify.EvAction.NOTIFY, "Raised for unit testing") + time.sleep(0.2) + (status, fvs) = self.eventStateTbl.get("state") + for fv in fvs: + if fv[0] == "events": + newEventCount = int(fv[1]) + if (eventCount+1) == newEventCount: + print("test_event success") + else: + print("ERROR: test_event failed" + " " + str(eventCount) + " " + str(newEventCount)) + + def raise_alarm(self): + print("\nTEST: raise alarm") + raisedCount = 0 + newRaisedCount = 0 + fvs = 0 + (status, fvs) = self.eventStateTbl.get("state") + for fv in fvs: + if fv[0] == "raised": + raisedCount = int(fv[1]) + + (status, fvs) = self.alarmStateTbl.get("state") + for fv in fvs: + if fv[0] == "alarms": + alarmCount = int(fv[1]) + if fv[0] == "warning": + warningCount = int(fv[1]) + + self.notify.logevent("DUMMY_ALARM", "test_raise_alarm", eventnotify.EvAction.RAISE_ALARM, "Raised for unit testing") + time.sleep(0.2) + + (status, fvs) = self.eventStateTbl.get("state") + for fv in fvs: + if fv[0] == "raised": + newRaisedCount = int(fv[1]) + + (status, fvs) = self.alarmStateTbl.get("state") + for fv in fvs: + if fv[0] == "alarms": + newAlarmCount = int(fv[1]) + if fv[0] == "warning": + newWarningCount = int(fv[1]) + + if (raisedCount+1) == newRaisedCount: + if (alarmCount + 1) == newAlarmCount: + if (warningCount + 1) == newWarningCount: + print("test_raise_alarm success") + else: + print("ERROR: test_raise_alarm severity count failed" + " " + str(warningCount) + " " + str(newWarningCount)) + else: + print("ERROR: test_raise_alarm alarm count failed" + " " + str(alarmCount) + " " + str(newAlarmCount)) + else: + print("ERROR: test_raise_alarm event count failed" + " " + str(raisedCount) + " " + str(newRaisedCount)) + + self.notify.logevent("DUMMY_ALARM", "test_raise_alarm", eventnotify.EvAction.CLEAR_ALARM, "Clearing for unit testing") + time.sleep(0.2) + + def clear_alarm(self): + print("\nTEST: raise and clear alarm") + self.notify.logevent("DUMMY_ALARM", "test_clear_alarm", eventnotify.EvAction.RAISE_ALARM, "Raised for unit testing") + time.sleep(0.2) + clearCount = 0 + newClearCount = 0 + fvs = 0 + (status, fvs) = self.eventStateTbl.get("state") + for fv in fvs: + if fv[0] == "cleared": + clearCount = int(fv[1]) + + (status, fvs) = self.alarmStateTbl.get("state") + for fv in fvs: + if fv[0] == "alarms": + alarmCount = int(fv[1]) + if fv[0] == "warning": + warningCount = int(fv[1]) + + self.notify.logevent("DUMMY_ALARM", "test_clear_alarm", eventnotify.EvAction.CLEAR_ALARM, "Clearing for unit testing") + time.sleep(0.2) + + (status, fvs) = self.eventStateTbl.get("state") + for fv in fvs: + if fv[0] == "cleared": + newClearCount = int(fv[1]) + + (status, fvs) = self.alarmStateTbl.get("state") + for fv in fvs: + if fv[0] == "alarms": + newAlarmCount = int(fv[1]) + if fv[0] == "warning": + newWarningCount = int(fv[1]) + + if (clearCount+1) == newClearCount: + if (alarmCount-1) == newAlarmCount: + if (warningCount-1) == newWarningCount: + print("test_clear_alarm success") + else: + print("ERROR: test_clear_alarm severity count failed" + " " + str(warningCount) + " " + str(newWarningCount)) + else: + print("ERROR: test_clear_alarm alarm count failed" + " " + str(alarmCount) + " " + str(newAlarmCount)) + else: + print("ERROR: test_clear_alarm failed" + " " + str(clearCount) + " " + str(newClearCount)) + + def ack_alarm(self): + print("\nTEST: ack alarm") + (status, fvs) = self.eventStateTbl.get("state") + ackCount = 0 + newAckCount = 0 + + for fv in fvs: + if fv[0] == "acked": + ackCount = int(fv[1]) + + (status, fvs) = self.alarmStateTbl.get("state") + for fv in fvs: + if fv[0] == "acknowledged": + alarmAckCount = int(fv[1]) + + self.notify.logevent("DUMMY_ALARM", "test_ack_alarm", eventnotify.EvAction.RAISE_ALARM, "RAISing for unit testing") + time.sleep(0.2) + + alm_keys = self.alarmTbl.getKeys() + print(alm_keys) + for key in alm_keys: + (status, fvs) = self.alarmTbl.get(key) + for fv in fvs: + if fv[1] == "test_ack_alarm": + raise_id = key + + print("raise_id is " + raise_id) + self.notify.logevent("", raise_id, eventnotify.EvAction.ACK_ALARM, "ACKNOWLEDGing for unit testing") + time.sleep(0.2) + + (status, fvs) = self.eventStateTbl.get("state") + for fv in fvs: + if fv[0] == "acked": + newAckCount = int(fv[1]) + + (status, fvs) = self.alarmStateTbl.get("state") + ackTime = "" + for fv in fvs: + if fv[0] == "acknowledged": + newAlarmAckCount = int(fv[1]) + if fv[0] == "acknowledge-time": + ackTime = fv[1] + + if (ackCount+1) == newAckCount: + if (alarmAckCount+1) == newAlarmAckCount: + if not ackTime: + print("test_ack_alarm success") + else: + print("ERROR: test_ack_alarm acknowedge-time not populated") + else: + print("ERROR: test_ack_alarm alarm-stats ack count failed" + " " + str(alarmAckCount) + " " + str(newAlarmAckCount)) + else: + print("ERROR: test_ack_alarm event-stats ack count failed" + " " + str(ackCount) + " " + str(newAckCount)) + + self.notify.logevent("DUMMY_ALARM", "test_ack_alarm", eventnotify.EvAction.CLEAR_ALARM, "CLEARing for unit testing") + + def unack_alarm(self): + print("\nTEST: unack alarm") + + self.notify.logevent("DUMMY_ALARM", "test_unack_alarm", eventnotify.EvAction.RAISE_ALARM, "RAISing for unit testing") + time.sleep(0.2) + + alm_keys = self.alarmTbl.getKeys() + print(alm_keys) + for key in alm_keys: + (status, fvs) = self.alarmTbl.get(key) + for fv in fvs: + if fv[1] == "test_unack_alarm": + raise_id = key + + print("raise_id is " + raise_id) + self.notify.logevent("", raise_id, eventnotify.EvAction.ACK_ALARM, "ACKNOWLEDGing for unit testing") + time.sleep(0.2) + + (status, fvs) = self.alarmStateTbl.get("state") + for fv in fvs: + if fv[0] == "acknowledged": + alarmAckCount = int(fv[1]) + + self.notify.logevent("", raise_id, eventnotify.EvAction.UNACK_ALARM, "UNACKNOWLEDGing for unit testing") + time.sleep(0.2) + + (status, fvs) = self.alarmStateTbl.get("state") + ackTime = "" + for fv in fvs: + if fv[0] == "acknowledged": + newAlarmAckCount = int(fv[1]) + if fv[0] == "acknowledge-time": + ackTime = fv[1] + + if (alarmAckCount-1) == newAlarmAckCount: + if not ackTime: + print("test_unack_alarm success") + else: + print("ERROR: test_unack_alarm acknowedge-time not populated") + else: + print("ERROR: test_unack_alarm alarm-stats ack count failed" + " " + str(alarmAckCount) + " " + str(newAlarmAckCount)) + + self.notify.logevent("DUMMY_ALARM", "test_unack_alarm", eventnotify.EvAction.CLEAR_ALARM, "CLEARing for unit testing") + + def evprofile_change_alarm_severity(self): + data = {} + data['events'] = [] + data['events'].append({ + 'name': 'DUMMY_ALARM', + 'severity': 'CRITICAL', + 'enable': 'true' + }) + + with open('unittest.json', 'w') as outfile: + json.dump(data, outfile) + + def evprofile_disable_event(self): + pass + +ut = EventUnitTest() +ut.unittest_init() +ut.test_event() +ut.raise_alarm() +ut.clear_alarm() +ut.ack_alarm() +ut.unack_alarm() + diff --git a/src/sonic-eventd/tests/eventnotify/__init__.py b/src/sonic-eventd/tests/eventnotify/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/src/sonic-eventd/tests/eventnotify/eventnotify.py b/src/sonic-eventd/tests/eventnotify/eventnotify.py new file mode 100644 index 000000000000..5327da942010 --- /dev/null +++ b/src/sonic-eventd/tests/eventnotify/eventnotify.py @@ -0,0 +1,44 @@ +from swsscommon import swsscommon +import time +import enum +import time +import os + +class EvAction(enum.Enum): + NOTIFY = 0 + CLEAR_ALARM = 1 + RAISE_ALARM = 2 + ACK_ALARM = 3 + UNACK_ALARM = 4 + +class EventNotify: + def __init__(self): + """Create a new Event Notify class.""" + self.event_db = swsscommon.DBConnector("EVENT_DB", 0, True) + self.evntPubTbl = swsscommon.Table(self.event_db, "EVENTPUBSUB") + self.evProfileTbl = swsscommon.Table(self.event_db, "EVPROFILE") + self.evntStateTbl = swsscommon.Table(self.event_db, "EVENT_STATS") + self.almStateTbl = swsscommon.Table(self.event_db, "ALARM_STATS") + self.sequence_id = 0 + + def getTicks(self): + return (time.time_ns()) + + def getActionStr(self, action): + if action == EvAction.CLEAR_ALARM: + return "CLEAR" + elif action == EvAction.RAISE_ALARM: + return "RAISE" + elif action == EvAction.ACK_ALARM: + return "ACKNOWLEDGE" + elif action == EvAction.UNACK_ALARM: + return "UNACKNOWLEDGE" + else: + return "" + + def logevent(self, name, source, action, message): + self.sequence_id = self.sequence_id + 1 + key = name + str(self.sequence_id) + actionStr = self.getActionStr(action) + fvs = swsscommon.FieldValuePairs([("time-created", str(self.getTicks())), ("type-id", name), ("text", message), ("action", actionStr), ("resource", source)]) + self.evntPubTbl.set(key, fvs) diff --git a/src/sonic-eventd/var/evprofile/default.json b/src/sonic-eventd/var/evprofile/default.json new file mode 100644 index 000000000000..40150a29583c --- /dev/null +++ b/src/sonic-eventd/var/evprofile/default.json @@ -0,0 +1,17 @@ +{ + "__README__" : "This is default map of events that eventd uses. Developer can modify this file and send SIGINT to eventd to make it read and use the updated file. Alternatively developer can test the new event by adding it to a custom event profile and use 'event profile ' command to apply that profile without having to send SIGINT to eventd. Developer need to commit default.json file with the new event after testing it out. Supported severities are: CRITICAL, MAJOR, MINOR, WARNING and INFORMATIONAL. Supported enable flag values are: true and false.", + "events":[ + { + "name" : "CUSTOM_EVPROFILE_CHANGE", + "severity" : "INFORMATIONAL", + "enable" : "true", + "message" : "Event Profile is applied." + }, + { + "name" : "DUMMY_ALARM", + "severity" : "WARNING", + "enable" : "true", + "message" : "Simulating an alarm." + } + ] +}