Implement sendTrap function
This function gets all the objects from the specific
notification class and send the snmp trap using the
netsnmp lib functions.
This commt adds the configure, makefile, bootstrap.sh,
clangformat file.
Add the unit test cases for ErrorNotification unit.
Change-Id: I2e982f18eb2745f2c0c8de0702cc85d12e80f6e3
Signed-off-by: Ratan Gupta <ratagupt@in.ibm.com>
diff --git a/Makefile.am b/Makefile.am
new file mode 100644
index 0000000..6807986
--- /dev/null
+++ b/Makefile.am
@@ -0,0 +1,32 @@
+# export these headers
+nobase_include_HEADERS = snmp_notification.hpp
+
+libsnmpdir = ${libdir}
+
+libsnmp_LTLIBRARIES = libsnmp.la
+libsnmp_la_SOURCES = \
+ snmp_notification.cpp
+
+libsnmp_la_LDFLAGS = \
+ $(SDBUSPLUS_LIBS) \
+ $(PHOSPHOR_DBUS_INTERFACES_LIBS) \
+ $(PHOSPHOR_LOGGING_LIBS) \
+ -lnetsnmp \
+ -lcrypto \
+ -version-info 0:0:0 -shared
+
+pkgconfiglibdir = ${libdir}/pkgconfig
+pkgconfiglib_DATA = phosphor-snmp.pc
+
+@CODE_COVERAGE_RULES@
+
+check_PROGRAMS =
+XFAIL_TESTS =
+
+include test/Makefile.am.include
+
+TESTS = $(check_PROGRAMS)
+
+clean-local: code-coverage-clean
+dist-clean-local: code-coverage-dist-clean
+
diff --git a/bootstrap.sh b/bootstrap.sh
new file mode 100755
index 0000000..9e232b6
--- /dev/null
+++ b/bootstrap.sh
@@ -0,0 +1,41 @@
+#!/bin/sh
+
+set -eu
+
+AUTOCONF_FILES="Makefile.in aclocal.m4 ar-lib autom4te.cache compile \
+ config.guess config.h.in config.sub configure depcomp install-sh \
+ ltmain.sh missing *libtool test-driver"
+
+BOOTSTRAP_MODE=""
+
+if [ $# -gt 0 ];
+then
+ BOOTSTRAP_MODE="${1}"
+ shift 1
+fi
+
+case ${BOOTSTRAP_MODE} in
+ clean)
+ test -f Makefile && make maintainer-clean
+ for file in ${AUTOCONF_FILES}; do
+ find -name "$file" | xargs -r rm -rf
+ done
+ exit 0
+ ;;
+esac
+
+autoreconf -i
+
+case ${BOOTSTRAP_MODE} in
+ dev)
+ ./configure \
+ ${CONFIGURE_FLAGS} \
+ --enable-code-coverage \
+ --enable-oe-sdk \
+ "$@"
+ ;;
+ *)
+ echo 'Run "./configure ${CONFIGURE_FLAGS} && make"'
+ ;;
+esac
+
diff --git a/configure.ac b/configure.ac
new file mode 100644
index 0000000..a32dbdc
--- /dev/null
+++ b/configure.ac
@@ -0,0 +1,55 @@
+AC_PREREQ([2.69])
+AC_INIT([phosphor-snmp], [1.0], [https://github.com/openbmc/openbmc/phosphor-snmp.git])
+AC_LANG([C++])
+AM_INIT_AUTOMAKE([subdir-objects -Wall -Werror foreign])
+AM_SILENT_RULES([yes])
+
+# Compiler flags
+CXXFLAGS="$CXXFLAGS -fpic -Wall -Werror"
+
+# Check for programs
+AC_PROG_CXX
+AM_PROG_AR
+AC_PROG_INSTALL
+AC_PROG_MAKE_SET
+
+LT_PREREQ([2.4.6])
+LT_INIT([dlopen disable-static shared])
+
+AC_CHECK_HEADERS([net-snmp/net-snmp-config.h],,\
+ AC_MSG_ERROR(["Requires net-snmp headers"]))
+
+# Checks for library functions
+LT_INIT # Required for systemd linking
+
+# Check/set gtest specific functions.
+AX_PTHREAD([GTEST_CPPFLAGS="-DGTEST_HAS_PTHREAD=1"],[GTEST_CPPFLAGS="-GTEST_HAS_PTHREAD=0"])
+AC_SUBST(GTEST_CPPFLAGS)
+
+# Add the code covererage
+AX_CODE_COVERAGE
+
+AC_ARG_ENABLE([oe-sdk],
+ AS_HELP_STRING([--enable-oe-sdk], [Link testcases absolutely against OE SDK so they can be ran within it.])
+)
+AC_ARG_VAR(OECORE_TARGET_SYSROOT,
+ [Path to the OE SDK SYSROOT])
+AS_IF([test "x$enable_oe_sdk" == "xyes"],
+ AS_IF([test "x$OECORE_TARGET_SYSROOT" == "x"],
+ AC_MSG_ERROR([OECORE_TARGET_SYSROOT must be set with --enable-oe-sdk])
+ )
+ AC_MSG_NOTICE([Enabling OE-SDK at $OECORE_TARGET_SYSROOT])
+ [
+ testcase_flags="-Wl,-rpath,\${OECORE_TARGET_SYSROOT}/lib"
+ testcase_flags="${testcase_flags} -Wl,-rpath,\${OECORE_TARGET_SYSROOT}/usr/lib"
+ testcase_flags="${testcase_flags} -Wl,-dynamic-linker,`find \${OECORE_TARGET_SYSROOT}/lib/ld-*.so | sort -r -n | head -n1`"
+ ]
+ AC_SUBST([OESDK_TESTCASE_FLAGS], [$testcase_flags])
+)
+
+
+AC_CONFIG_FILES([Makefile])
+AC_CONFIG_FILES([phosphor-snmp.pc])
+AC_OUTPUT
+
+
diff --git a/phosphor-snmp.pc.in b/phosphor-snmp.pc.in
new file mode 100644
index 0000000..c0e2397
--- /dev/null
+++ b/phosphor-snmp.pc.in
@@ -0,0 +1,13 @@
+prefix=@prefix@
+exec_prefix=@exec_prefix@
+libdir=@libdir@
+includedir=@includedir@
+
+Name: phosphor-snmp
+Description: Phosphor snmp utilities
+URL: https://github.com/openbmc/phosphor-snmp
+Version: @VERSION@
+Requires: @AX_PACKAGE_REQUIRES@
+Requires.private: @AX_PACKAGE_REQUIRES_PRIVATE@
+Libs: -L@libdir@ -lsnmp
+Cflags: -I@includedir@
diff --git a/snmp_notification.cpp b/snmp_notification.cpp
new file mode 100644
index 0000000..e27408a
--- /dev/null
+++ b/snmp_notification.cpp
@@ -0,0 +1,142 @@
+#include "snmp_notification.hpp"
+
+#include <phosphor-logging/elog-errors.hpp>
+#include <phosphor-logging/log.hpp>
+
+#include "xyz/openbmc_project/Common/error.hpp"
+
+namespace phosphor
+{
+namespace network
+{
+namespace snmp
+{
+
+using namespace phosphor::logging;
+using namespace sdbusplus::xyz::openbmc_project::Common::Error;
+
+using snmpSessionPtr =
+ std::unique_ptr<netsnmp_session, decltype(&::snmp_close)>;
+
+bool Notification::addPDUVar(netsnmp_pdu& pdu, const OID& objID,
+ size_t objIDLen, u_char type, Value val)
+{
+ netsnmp_variable_list* varList = nullptr;
+ switch (type)
+ {
+ case ASN_INTEGER:
+ {
+ auto ltmp = val.get<int32_t>();
+ varList = snmp_pdu_add_variable(&pdu, objID.data(), objIDLen, type,
+ <mp, sizeof(ltmp));
+ }
+ break;
+ case ASN_UNSIGNED:
+ {
+ auto ltmp = val.get<uint32_t>();
+ varList = snmp_pdu_add_variable(&pdu, objID.data(), objIDLen, type,
+ <mp, sizeof(ltmp));
+ }
+ break;
+ case ASN_OPAQUE_U64:
+ {
+ auto ltmp = val.get<uint64_t>();
+ varList = snmp_pdu_add_variable(&pdu, objID.data(), objIDLen, type,
+ <mp, sizeof(ltmp));
+ }
+ break;
+ case ASN_OCTET_STR:
+ {
+ auto value = val.get<std::string>();
+ varList = snmp_pdu_add_variable(&pdu, objID.data(), objIDLen, type,
+ value.c_str(), value.length());
+ }
+ break;
+ }
+ return (varList == nullptr ? false : true);
+}
+
+void Notification::sendTrap()
+{
+ log<level::DEBUG>("Sending SNMP Trap");
+
+ constexpr auto comm = "public";
+ constexpr auto localHost = "127.0.0.1";
+ netsnmp_session session{0};
+
+ snmp_sess_init(&session);
+
+ init_snmp("snmpapp");
+
+ // TODO: https://github.com/openbmc/openbmc/issues/3145
+ session.version = SNMP_VERSION_2c;
+ session.community = (u_char*)comm;
+ session.community_len = strlen(comm);
+ session.callback = nullptr;
+ session.callback_magic = nullptr;
+
+ // TODO:- get it from settings D-bus object.
+ session.peername = const_cast<char*>(localHost);
+
+ // create the session
+ auto ss = snmp_add(
+ &session, netsnmp_transport_open_client("snmptrap", session.peername),
+ nullptr, nullptr);
+ if (!ss)
+ {
+ log<level::ERR>("Unable to get the snmp session.",
+ entry("SNMPMANAGER=%s", session.peername));
+ elog<InternalFailure>();
+ }
+
+ // Wrap the raw pointer in RAII
+ snmpSessionPtr sessionPtr(ss, &::snmp_close);
+
+ ss = nullptr;
+
+ auto pdu = snmp_pdu_create(SNMP_MSG_TRAP2);
+ if (!pdu)
+ {
+ log<level::ERR>("Failed to create notification PDU");
+ elog<InternalFailure>();
+ }
+
+ pdu->trap_type = SNMP_TRAP_ENTERPRISESPECIFIC;
+
+ auto trapInfo = getTrapOID();
+
+ if (!snmp_pdu_add_variable(
+ pdu, SNMPTrapOID, sizeof(SNMPTrapOID) / sizeof(oid), ASN_OBJECT_ID,
+ trapInfo.first.data(), trapInfo.second * sizeof(oid)))
+ {
+ log<level::ERR>("Failed to add the SNMP var(trapID)");
+ snmp_free_pdu(pdu);
+ elog<InternalFailure>();
+ }
+
+ auto objectList = getFieldOIDList();
+
+ for (const auto& object : objectList)
+ {
+ if (!addPDUVar(*pdu, std::get<0>(object), std::get<1>(object),
+ std::get<2>(object), std::get<3>(object)))
+ {
+ log<level::ERR>("Failed to add the SNMP var");
+ snmp_free_pdu(pdu);
+ elog<InternalFailure>();
+ }
+ }
+
+ // pdu is freed by snmp_send
+ if (!snmp_send(sessionPtr.get(), pdu))
+ {
+ log<level::ERR>("Failed to send the snmp trap.");
+ elog<InternalFailure>();
+ }
+
+ log<level::DEBUG>("Sent SNMP Trap");
+}
+
+} // namespace snmp
+} // namespace network
+} // namespace phosphor
diff --git a/snmp_notification.hpp b/snmp_notification.hpp
index d21e68c..07a8518 100644
--- a/snmp_notification.hpp
+++ b/snmp_notification.hpp
@@ -107,6 +107,17 @@
void sendTrap();
protected:
+ /** @brief Add the variable in the snmp pdu object.
+ * @param[in] pdu - SNMP pdu object.
+ * @param[in] objID - SNMP object identifier.
+ * @param[in] objIDLen - Object identifier length.
+ * @param[in] type - ASN type of object.
+ * @param[in] val - Value of the object.
+ * @returns true on success otherwise false.
+ */
+ bool addPDUVar(netsnmp_pdu &pdu, const OID &objID, size_t objIDLen,
+ u_char type, Value val);
+
/** @brief get the SNMP notification type in the mib
* defined format.
* This is pure virtual function all the subclasses
@@ -121,6 +132,8 @@
virtual std::vector<Object> getFieldOIDList() = 0;
};
+class TestErrorNotification;
+
/** @class ErrorNotification
* @brief subclass of Notification
*
@@ -203,6 +216,8 @@
return objectList;
}
+
+ friend class TestErrorNotification;
};
} // namespace snmp
diff --git a/test/Makefile.am.include b/test/Makefile.am.include
new file mode 100644
index 0000000..9c5435a
--- /dev/null
+++ b/test/Makefile.am.include
@@ -0,0 +1,18 @@
+AM_CPPFLAGS = \
+ -I${top_srcdir} \
+ -I${top_builddir} \
+ -Igtest \
+ $(GTEST_CPPFLAGS) \
+ $(CODE_COVERAGE_CPPFLAGS)
+
+AM_LDFLAGS = -lgtest_main -lgtest -lstdc++fs \
+ $(OESDK_TESTCASE_FLAGS) \
+ $(CODE_COVERAGE_LIBS)
+
+AM_CXXFLAGS = $(PTHREAD_CFLAGS) \
+ $(CODE_COVERAGE_CXXFLAGS)
+
+test_notification_SOURCES = \
+ %reldir%/test_error_notification.cpp
+
+check_PROGRAMS += %reldir%/notification
diff --git a/test/test_error_notification.cpp b/test/test_error_notification.cpp
new file mode 100644
index 0000000..4556a3e
--- /dev/null
+++ b/test/test_error_notification.cpp
@@ -0,0 +1,60 @@
+#include "snmp_notification.hpp"
+#include <gtest/gtest.h>
+#include <netinet/in.h>
+
+namespace phosphor
+{
+namespace network
+{
+namespace snmp
+{
+
+constexpr auto ERROR_NOTIF_FIELD_COUNT = 4;
+
+class TestErrorNotification : public testing::Test
+{
+ public:
+ OBMCErrorNotification notif;
+ TestErrorNotification()
+ {
+ // Empty
+ }
+ std::vector<Object> getFieldOIDList()
+ {
+ return notif.getFieldOIDList();
+ }
+};
+
+TEST_F(TestErrorNotification, VerifyErrorNotificationFields)
+{
+ auto oidList = getFieldOIDList();
+
+ // verify the number of the fields in the notification.
+ EXPECT_EQ(ERROR_NOTIF_FIELD_COUNT, oidList.size());
+
+ // Verify the type of each field.
+ EXPECT_EQ(ASN_UNSIGNED, std::get<Type>(oidList[0]));
+
+ EXPECT_EQ(ASN_OPAQUE_U64, std::get<Type>(oidList[1]));
+ EXPECT_EQ(ASN_INTEGER, std::get<Type>(oidList[2]));
+ EXPECT_EQ(ASN_OCTET_STR, std::get<Type>(oidList[3]));
+}
+
+TEST_F(TestErrorNotification, GetASNType)
+{
+ auto type = getASNType<uint32_t>();
+ EXPECT_EQ(ASN_UNSIGNED, type);
+
+ type = getASNType<uint64_t>();
+ EXPECT_EQ(ASN_OPAQUE_U64, type);
+
+ type = getASNType<int32_t>();
+ EXPECT_EQ(ASN_INTEGER, type);
+
+ type = getASNType<std::string>();
+ EXPECT_EQ(ASN_OCTET_STR, type);
+}
+
+} // namespec snmp
+} // namespce network
+} // namespace phosphor