Implement the Client create interface
This commit also implement the D-Bus service which would be
used for snmp client configuration and would add the
snmp manager/trap receiver D-Bus objects under
namespace /xyz/openbmc_project/network/snmp/manager/
It implements the delete interface for SNMP client D-Bus Object.
Resolves openbmc/openbmc#3057
Change-Id: I0df7b1475f81325b9ac497c1fb350c3f62329686
Signed-off-by: Ratan Gupta <ratagupt@in.ibm.com>
diff --git a/Makefile.am b/Makefile.am
index e177077..7167b1d 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -2,6 +2,51 @@
nobase_include_HEADERS = snmp.hpp \
snmp_notification.hpp
+nobase_nodist_include_HEADERS = \
+ xyz/openbmc_project/Network/Client/Create/server.hpp
+
+sbin_PROGRAMS = phosphor-network-snmpconf
+
+noinst_HEADERS = \
+ snmp_client.hpp \
+ snmp_conf_manager.hpp
+
+phosphor_network_snmpconf_SOURCES = \
+ snmp_main.cpp \
+ snmp_conf_manager.cpp \
+ snmp_client.cpp \
+ xyz/openbmc_project/Network/Client/Create/server.cpp
+
+CLEANFILES = \
+ xyz/openbmc_project/Network/Client/Create/server.cpp \
+ xyz/openbmc_project/Network/Client/Create/server.hpp
+
+BUILT_SOURCES = \
+ xyz/openbmc_project/Network/Client/Create/server.cpp \
+ xyz/openbmc_project/Network/Client/Create/server.hpp
+
+phosphor_network_snmpconf_LDFLAGS = \
+ $(SYSTEMD_LIBS) \
+ $(SDBUSPLUS_LIBS) \
+ $(PHOSPHOR_DBUS_INTERFACES_LIBS) \
+ $(PHOSPHOR_LOGGING_LIBS) \
+ -lstdc++fs
+
+phosphor_network_snmpconf_CXXFLAGS = \
+ $(SYSTEMD_CFLAGS) \
+ $(SDBUSPLUS_CFLAGS) \
+ $(PHOSPHOR_DBUS_INTERFACES_CFLAGS) \
+ $(PHOSPHOR_LOGGING_CFLAGS)
+
+xyz/openbmc_project/Network/Client/Create/server.cpp: xyz/openbmc_project/Network/Client/Create.interface.yaml xyz/openbmc_project/Network/Client/Create/server.hpp
+ @mkdir -p `dirname $@`
+ $(SDBUSPLUSPLUS) -r $(srcdir) interface server-cpp xyz.openbmc_project.Network.Client.Create > $@
+
+xyz/openbmc_project/Network/Client/Create/server.hpp: xyz/openbmc_project/Network/Client/Create.interface.yaml
+ @mkdir -p `dirname $@`
+ $(SDBUSPLUSPLUS) -r $(srcdir) interface server-header xyz.openbmc_project.Network.Client.Create > $@
+ sed -i '5i #include \"xyz\/openbmc_project\/Network\/Client\/server.hpp\"' $@
+
libsnmpdir = ${libdir}
libsnmp_LTLIBRARIES = libsnmp.la
diff --git a/configure.ac b/configure.ac
index a32dbdc..a0cdbe2 100644
--- a/configure.ac
+++ b/configure.ac
@@ -12,13 +12,43 @@
AM_PROG_AR
AC_PROG_INSTALL
AC_PROG_MAKE_SET
+AC_PROG_MKDIR_P
LT_PREREQ([2.4.6])
LT_INIT([dlopen disable-static shared])
+# Checks for libraries.
+AX_PKG_CHECK_MODULES([SYSTEMD], [], [libsystemd >= 221], [],\
+[AC_MSG_ERROR(["Systemd version should be greater then 221."])])
+
+AX_PKG_CHECK_MODULES([SDBUSPLUS], [sdbusplus], [], [],\
+[AC_MSG_ERROR(["Requires sdbusplus package."])])
+
+
+AC_PATH_PROG([SDBUSPLUSPLUS], [sdbus++])
+
+AX_PKG_CHECK_MODULES([PHOSPHOR_LOGGING], [], [phosphor-logging], [],\
+[AC_MSG_ERROR(["Requires phosphor-logging package."])])
+
+# Checks for header files.
+AC_CHECK_HEADER(systemd/sd-bus.h, ,\
+[AC_MSG_ERROR([Could not find systemd/sd-bus.h...systemd development package required])])
+
+AX_PKG_CHECK_MODULES([PHOSPHOR_DBUS_INTERFACES], [], [phosphor-dbus-interfaces],\
+[], [AC_MSG_ERROR(["phosphor-dbus-interfaces required and not found."])])
+
AC_CHECK_HEADERS([net-snmp/net-snmp-config.h],,\
AC_MSG_ERROR(["Requires net-snmp headers"]))
+AC_ARG_VAR(BUSNAME_NETWORK_SNMP, [The Dbus busname to own])
+AS_IF([test "x$BUSNAME_NETWORK_SNMP" == "x"], [BUSNAME_NETWORK_SNMP="xyz.openbmc_project.Network.SNMP"])
+AC_DEFINE_UNQUOTED([BUSNAME_NETWORK_SNMP], ["$BUSNAME_NETWORK_SNMP"], [The DBus busname to own])
+
+AC_ARG_VAR(OBJ_NETWORK_SNMP, [The network snmp root DBus object path])
+AS_IF([test "x$OBJ_NETWORK_SNMP" == "x"], [OBJ_NETWORK_SNMP="/xyz/openbmc_project/network/snmp/manager"])
+AC_DEFINE_UNQUOTED([OBJ_NETWORK_SNMP], ["$OBJ_NETWORK_SNMP"], [The network snmp root DBus object path])
+
+
# Checks for library functions
LT_INIT # Required for systemd linking
@@ -47,9 +77,8 @@
AC_SUBST([OESDK_TESTCASE_FLAGS], [$testcase_flags])
)
-
+# Create configured output
+AC_CONFIG_HEADERS([config.h])
AC_CONFIG_FILES([Makefile])
AC_CONFIG_FILES([phosphor-snmp.pc])
AC_OUTPUT
-
-
diff --git a/snmp_client.cpp b/snmp_client.cpp
new file mode 100644
index 0000000..4049e2d
--- /dev/null
+++ b/snmp_client.cpp
@@ -0,0 +1,30 @@
+#include "snmp_client.hpp"
+#include "snmp_conf_manager.hpp"
+
+namespace phosphor
+{
+namespace network
+{
+namespace snmp
+{
+
+Client::Client(sdbusplus::bus::bus& bus, const char* objPath,
+ ConfManager& parent, const std::string& address, uint16_t port) :
+ Ifaces(bus, objPath, true),
+ parent(parent)
+{
+ this->address(address);
+ this->port(port);
+
+ // Emit deferred signal.
+ emit_object_added();
+}
+
+void Client::delete_()
+{
+ parent.deleteSNMPClient(this->address());
+}
+
+} // namespace snmp
+} // namespace network
+} // namespace phosphor
diff --git a/snmp_client.hpp b/snmp_client.hpp
new file mode 100644
index 0000000..5b27f1b
--- /dev/null
+++ b/snmp_client.hpp
@@ -0,0 +1,60 @@
+#pragma once
+
+#include "xyz/openbmc_project/Network/Client/server.hpp"
+#include "xyz/openbmc_project/Object/Delete/server.hpp"
+
+#include <sdbusplus/bus.hpp>
+#include <sdbusplus/server/object.hpp>
+
+#include <string>
+
+namespace phosphor
+{
+namespace network
+{
+namespace snmp
+{
+
+class ConfManager;
+
+using Ifaces = sdbusplus::server::object::object<
+ sdbusplus::xyz::openbmc_project::Network::server::Client,
+ sdbusplus::xyz::openbmc_project::Object::server::Delete>;
+
+/** @class Client
+ * @brief represents the snmp client configuration
+ * @details A concrete implementation for the
+ * xyz.openbmc_project.Network.Client Dbus interface.
+ */
+class Client : public Ifaces
+{
+ public:
+ Client() = delete;
+ Client(const Client &) = delete;
+ Client &operator=(const Client &) = delete;
+ Client(Client &&) = delete;
+ Client &operator=(Client &&) = delete;
+ virtual ~Client() = default;
+
+ /** @brief Constructor to put object onto bus at a dbus path.
+ * @param[in] bus - Bus to attach to.
+ * @param[in] objPath - Path to attach at.
+ * @param[in] parent - Parent D-bus Object.
+ * @param[in] address - IPaddress/Hostname.
+ * @param[in] port - network port.
+ */
+ Client(sdbusplus::bus::bus &bus, const char *objPath, ConfManager &parent,
+ const std::string &address, uint16_t port);
+
+ /** @brief Delete this d-bus object.
+ */
+ void delete_() override;
+
+ private:
+ /** @brief Parent D-Bus Object. */
+ ConfManager &parent;
+};
+
+} // namespace snmp
+} // namespace network
+} // namespace phosphor
diff --git a/snmp_conf_manager.cpp b/snmp_conf_manager.cpp
new file mode 100644
index 0000000..30d0815
--- /dev/null
+++ b/snmp_conf_manager.cpp
@@ -0,0 +1,61 @@
+#include "config.h"
+#include "snmp_conf_manager.hpp"
+#include <phosphor-logging/log.hpp>
+
+#include <experimental/filesystem>
+
+namespace phosphor
+{
+namespace network
+{
+namespace snmp
+{
+
+using namespace phosphor::logging;
+
+ConfManager::ConfManager(sdbusplus::bus::bus& bus, const char* objPath) :
+ details::CreateIface(bus, objPath, true), bus(bus), objectPath(objPath)
+{
+}
+
+void ConfManager::client(std::string address, uint16_t port)
+{
+ auto clientEntry = this->clients.find(address);
+ if (clientEntry == this->clients.end())
+ {
+ std::experimental::filesystem::path objPath;
+ objPath /= objectPath;
+ objPath /= generateId(address, port);
+
+ this->clients.emplace(
+ address, std::make_unique<phosphor::network::snmp::Client>(
+ bus, objPath.string().c_str(), *this, address, port));
+ }
+}
+
+std::string ConfManager::generateId(const std::string& address, uint16_t port)
+{
+ std::stringstream hexId;
+ std::string hashString = address;
+ hashString += std::to_string(port);
+
+ // Only want 8 hex digits.
+ hexId << std::hex << ((std::hash<std::string>{}(hashString)) & 0xFFFFFFFF);
+ return hexId.str();
+}
+
+void ConfManager::deleteSNMPClient(const std::string& address)
+{
+ auto it = clients.find(address);
+ if (it == clients.end())
+ {
+ log<level::ERR>("Unable to delete the snmp client.",
+ entry("ADDRESS=%s", address.c_str()));
+ return;
+ }
+ this->clients.erase(it);
+}
+
+} // namespace snmp
+} // namespace network
+} // namespace phosphor
diff --git a/snmp_conf_manager.hpp b/snmp_conf_manager.hpp
new file mode 100644
index 0000000..0f1ee4b
--- /dev/null
+++ b/snmp_conf_manager.hpp
@@ -0,0 +1,81 @@
+#pragma once
+
+#include "snmp_client.hpp"
+
+#include <xyz/openbmc_project/Network/Client/Create/server.hpp>
+#include <sdbusplus/bus.hpp>
+
+#include <string>
+
+namespace phosphor
+{
+namespace network
+{
+namespace snmp
+{
+using IPAddress = std::string;
+using ClientList = std::map<IPAddress, std::unique_ptr<Client>>;
+
+namespace details
+{
+
+using CreateIface = sdbusplus::server::object::object<
+ sdbusplus::xyz::openbmc_project::Network::Client::server::Create>;
+
+} // namespace details
+
+class TestSNMPConfManager;
+/** @class Manager
+ * @brief OpenBMC SNMP config implementation.
+ */
+class ConfManager : public details::CreateIface
+{
+ public:
+ ConfManager() = delete;
+ ConfManager(const ConfManager&) = delete;
+ ConfManager& operator=(const ConfManager&) = delete;
+ ConfManager(ConfManager&&) = delete;
+ ConfManager& operator=(ConfManager&&) = delete;
+ virtual ~ConfManager() = default;
+
+ /** @brief Constructor to put object onto bus at a dbus path.
+ * @param[in] bus - Bus to attach to.
+ * @param[in] objPath - Path to attach at.
+ */
+ ConfManager(sdbusplus::bus::bus& bus, const char* objPath);
+
+ /** @brief Function to create snmp manager details D-Bus object.
+ * @param[in] address- IP address/Hostname.
+ * @param[in] port - network port.
+ */
+ void client(std::string address, uint16_t port) override;
+
+ /* @brief delete the dbus object of the given ipaddress.
+ * @param[in] address - IP address/Hostname.
+ */
+ void deleteSNMPClient(const std::string& address);
+
+ protected:
+ /** @brief generates the id by doing hash of ipaddress, port
+ * @param[in] address - IP address/Hostname.
+ * @param[in] port - network port.
+ * @return hash string.
+ */
+ static std::string generateId(const std::string& address, uint16_t port);
+
+ private:
+ /** @brief sdbusplus DBus bus object. */
+ sdbusplus::bus::bus& bus;
+
+ /** @brief Path of Object. */
+ std::string objectPath;
+
+ /** @brief map of IPAddress dbus objects and their names */
+ ClientList clients;
+
+ friend class TestSNMPConfManager;
+};
+
+} // namespace snmp
+} // namespace network
+} // namespace phosphor
diff --git a/snmp_main.cpp b/snmp_main.cpp
new file mode 100644
index 0000000..932b9f4
--- /dev/null
+++ b/snmp_main.cpp
@@ -0,0 +1,49 @@
+#include "config.h"
+#include "snmp_conf_manager.hpp"
+
+#include <phosphor-logging/log.hpp>
+#include <sdbusplus/bus.hpp>
+#include <sdbusplus/server/manager.hpp>
+
+#include <memory>
+
+/* Need a custom deleter for freeing up sd_event */
+struct EventDeleter
+{
+ void operator()(sd_event* event) const
+ {
+ event = sd_event_unref(event);
+ }
+};
+
+using EventPtr = std::unique_ptr<sd_event, EventDeleter>;
+
+int main(int argc, char* argv[])
+{
+ using namespace phosphor::logging;
+
+ auto bus = sdbusplus::bus::new_default();
+
+ sd_event* event = nullptr;
+ auto r = sd_event_default(&event);
+ if (r < 0)
+ {
+ log<level::ERR>("Error creating a default sd_event handler");
+ return r;
+ }
+
+ EventPtr eventPtr{event};
+ event = nullptr;
+
+ // Attach the bus to sd_event to service user requests
+ bus.attach_event(eventPtr.get(), SD_EVENT_PRIORITY_NORMAL);
+
+ // Add sdbusplus Object Manager for the 'root' path of the snmp.
+ sdbusplus::server::manager::manager objManager(bus, OBJ_NETWORK_SNMP);
+ bus.request_name(BUSNAME_NETWORK_SNMP);
+
+ auto manager = std::make_unique<phosphor::network::snmp::ConfManager>(
+ bus, OBJ_NETWORK_SNMP);
+
+ return sd_event_loop(eventPtr.get());
+}
diff --git a/test/Makefile.am.include b/test/Makefile.am.include
index 9c5435a..7926d09 100644
--- a/test/Makefile.am.include
+++ b/test/Makefile.am.include
@@ -2,17 +2,37 @@
-I${top_srcdir} \
-I${top_builddir} \
-Igtest \
- $(GTEST_CPPFLAGS) \
- $(CODE_COVERAGE_CPPFLAGS)
+ $(GTEST_CPPFLAGS)
-AM_LDFLAGS = -lgtest_main -lgtest -lstdc++fs \
+AM_CFLAGS = \
+ $(PTHREAD_CFLAGS) \
+ $(CODE_COVERAGE_CFLAGS) \
+ $(SDBUSPLUS_CFLAGS) \
+ $(PHOSPHOR_LOGGING_CFLAGS) \
+ $(PHOSPHOR_DBUS_INTERFACES_CFLAGS)
+
+AM_CXXFLAGS = \
+ $(PTHREAD_CXXFLAGS) \
+ $(CODE_COVERAGE_CXXFLAGS) \
+ $(SDBUSPLUS_CXXFLAGS) \
+ $(PHOSPHOR_LOGGING_CXXFLAGS) \
+ $(PHOSPHOR_DBUS_INTERFACES_CXXFLAGS)
+
+AM_LDFLAGS = \
+ -lgtest_main -lgtest -lstdc++fs \
+ $(PTHREAD_CFLAGS) \
+ $(SDBUSPLUS_LIBS) \
+ $(PHOSPHOR_LOGGING_LIBS) \
+ $(PHOSPHOR_DBUS_INTERFACES_LIBS) \
$(OESDK_TESTCASE_FLAGS) \
$(CODE_COVERAGE_LIBS)
-AM_CXXFLAGS = $(PTHREAD_CFLAGS) \
- $(CODE_COVERAGE_CXXFLAGS)
-
test_notification_SOURCES = \
- %reldir%/test_error_notification.cpp
+ %reldir%/test_error_notification.cpp \
+ %reldir%/test_snmp_conf_manager.cpp
+
+test_notification_LDADD = $(top_builddir)/phosphor_network_snmpconf-snmp_conf_manager.o \
+ $(top_builddir)/phosphor_network_snmpconf-snmp_client.o \
+ $(top_builddir)/xyz/openbmc_project/Network/Client/Create/phosphor_network_snmpconf-server.o
check_PROGRAMS += %reldir%/notification
diff --git a/test/test_snmp_conf_manager.cpp b/test/test_snmp_conf_manager.cpp
new file mode 100644
index 0000000..ab29215
--- /dev/null
+++ b/test/test_snmp_conf_manager.cpp
@@ -0,0 +1,91 @@
+#include "snmp_conf_manager.hpp"
+
+#include "xyz/openbmc_project/Common/error.hpp"
+
+#include <gtest/gtest.h>
+#include <sdbusplus/bus.hpp>
+
+namespace phosphor
+{
+namespace network
+{
+namespace snmp
+{
+
+class TestSNMPConfManager : public testing::Test
+{
+ public:
+ sdbusplus::bus::bus bus;
+ ConfManager manager;
+ std::string confDir;
+ TestSNMPConfManager() :
+ bus(sdbusplus::bus::new_default()), manager(bus, ""){};
+
+ ~TestSNMPConfManager()
+ {
+ }
+
+ void createSNMPClient(std::string ipaddress, uint16_t port)
+ {
+ manager.client(ipaddress, port);
+ }
+
+ ClientList &getSNMPClients()
+ {
+ return manager.clients;
+ }
+
+ void deleteSNMPClient(std::string ipaddress)
+ {
+ if (manager.clients.find(ipaddress) != manager.clients.end())
+ {
+ auto &it = manager.clients[ipaddress];
+ it->delete_();
+ }
+ }
+};
+
+// Add single SNMP client
+TEST_F(TestSNMPConfManager, AddSNMPClient)
+{
+ using namespace sdbusplus::xyz::openbmc_project::Common::Error;
+
+ createSNMPClient("192.168.1.1", 24);
+
+ auto &clients = getSNMPClients();
+ EXPECT_EQ(1, clients.size());
+ EXPECT_EQ(true, clients.find("192.168.1.1") != clients.end());
+}
+
+// Add multiple SNMP client
+TEST_F(TestSNMPConfManager, AddMultipleSNMPClient)
+{
+ using namespace sdbusplus::xyz::openbmc_project::Common::Error;
+
+ createSNMPClient("192.168.1.1", 24);
+ createSNMPClient("192.168.1.2", 24);
+
+ auto &clients = getSNMPClients();
+ EXPECT_EQ(2, clients.size());
+ EXPECT_EQ(true, clients.find("192.168.1.1") != clients.end());
+ EXPECT_EQ(true, clients.find("192.168.1.2") != clients.end());
+}
+
+// Delete SNMP client
+TEST_F(TestSNMPConfManager, DeleteSNMPClient)
+{
+ using namespace sdbusplus::xyz::openbmc_project::Common::Error;
+
+ createSNMPClient("192.168.1.1", 24);
+ createSNMPClient("192.168.1.2", 24);
+
+ auto &clients = getSNMPClients();
+ EXPECT_EQ(2, clients.size());
+ deleteSNMPClient("192.168.1.1");
+ EXPECT_EQ(1, clients.size());
+ EXPECT_EQ(true, clients.find("192.168.1.2") != clients.end());
+}
+
+} // namespace snmp
+} // namespce network
+} // namespace phosphor