summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNikita Kosenkov <NKosenkov@IBS.RU>2022-09-13 14:31:56 +0300
committerNikita Kosenkov <NKosenkov@IBS.RU>2022-09-13 14:33:45 +0300
commitcae1d44c2db44b1c76bc85a45e5577f4fd7eeb59 (patch)
treef8ea622602c9d4000640d8314951ae597b6afeb2
parentca7e7213b42f0377eb80491ad1e334ed5c24c8cd (diff)
downloadopenbmc-cae1d44c2db44b1c76bc85a45e5577f4fd7eeb59.tar.xz
bmcweb: Added patch to manage SNMPv2 trap receivers in Redfish
-rw-r--r--meta-ibs/meta-cp2-5422/recipes-phosphor/interfaces/bmcweb/0015-Redfish-Implement-SNMP-Trap.patch705
-rw-r--r--meta-ibs/meta-cp2-5422/recipes-phosphor/interfaces/bmcweb_%.bbappend1
2 files changed, 706 insertions, 0 deletions
diff --git a/meta-ibs/meta-cp2-5422/recipes-phosphor/interfaces/bmcweb/0015-Redfish-Implement-SNMP-Trap.patch b/meta-ibs/meta-cp2-5422/recipes-phosphor/interfaces/bmcweb/0015-Redfish-Implement-SNMP-Trap.patch
new file mode 100644
index 0000000000..683075b042
--- /dev/null
+++ b/meta-ibs/meta-cp2-5422/recipes-phosphor/interfaces/bmcweb/0015-Redfish-Implement-SNMP-Trap.patch
@@ -0,0 +1,705 @@
+From 47ba142a47966b9c679a806468a58e0b434ce915 Mon Sep 17 00:00:00 2001
+From: Nikita Kosenkov <NKosenkov@IBS.RU>
+Date: Tue, 13 Sep 2022 13:38:49 +0300
+Subject: [PATCH] Redfish: Implement SNMP Trap
+
+Implement SNMPTrap in EventDestination of Redfish. We can use
+this Redfish interface to add/get/delete the SNMPTrap port and
+destination address. When the error log is generated, phosphor-snmp
+will send SNMPTrap messages to our configured SNMPTrap destination.
+
+The MIB is here:
+https://github.com/openbmc/phosphor-snmp/blob/master/mibs/NotificationMIB.txt
+
+Refer:
+https://www.dmtf.org/sites/default/files/standards/documents/DSP0268_2019.3.pdf
+
+SNMPTrap test: Tested ok on the Witherspoon machine.
+Steps are as follows:
+1. Use this Redfish interface to configure the port and
+ destination address:
+ curl -k -H "X-Auth-Token: $token" -X POST https://${bmc}/redfish/v1/EventService/Subscriptions -d '{"Destination": "snmp://192.168.31.89:162", "SubscriptionType": "SNMPTrap", "Protocol": "SNMPv2c"}'
+2. Run the SNMPTrap receiver tool in the destination
+ computer(192.168.31.89),I used iReasoning MIB Browser as the
+ SNMPTrap receiving tool.
+3. Trigger error logs such as power supply AC Lost. We will see
+ the error log under /xyz/openbmc_project/logging.
+4. The SNMPTrap receiver tool in the destination computer received
+ the SNMPTrap sent by OpenBMC.
+
+Tested: Validator passes
+1. Add snmp client:
+ curl -k -H "X-Auth-Token: $token" -X POST https://${bmc}/redfish/v1/EventService/Subscriptions -d '{"Destination": "snmp://192.168.31.89:162", "SubscriptionType": "SNMPTrap", "Protocol": "SNMPv2c", "Context": "testContext"}'
+ {
+ "@Message.ExtendedInfo": [
+ {
+ "@odata.type": "#Message.v1_0_0.Message",
+ "Message": "The resource has been created successfully",
+ "MessageArgs": [],
+ "MessageId": "Base.1.8.1.Created",
+ "MessageSeverity": "OK",
+ "Resolution": "None"
+ }
+ ]
+ }
+2. Get snmp trap client configurations:
+curl -k -H "X-Auth-Token: $token" -X GET https://${bmc}/redfish/v1/EventService/Subscriptions/snmp1
+{
+ "@odata.id": "/redfish/v1/EventService/Subscriptions/snmp1",
+ "@odata.type": "#EventDestination.v1_7_0.EventDestination",
+ "Context": "testContext",
+ "Destination": "snmp://192.168.31.89:162",
+ "EventFormatType": "Event",
+ "Id": "snmp1",
+ "Name": "Event Destination snmp1",
+ "Protocol": "SNMPv2c",
+ "SubscriptionType": "SNMPTrap"
+}
+
+Reboot the BMC, and get the snmp trap client again:
+curl -k -H "X-Auth-Token: $token" -X GET https://${bmc}/redfish/v1/EventService/Subscriptions/snmp1
+{
+ "@odata.id": "/redfish/v1/EventService/Subscriptions/snmp1",
+ "@odata.type": "#EventDestination.v1_7_0.EventDestination",
+ "Context": "testContext",
+ "Destination": "snmp://192.168.31.89:162",
+ "EventFormatType": "Event",
+ "Id": "snmp1",
+ "Name": "Event Destination snmp1",
+ "Protocol": "SNMPv2c",
+ "SubscriptionType": "SNMPTrap"
+}
+
+3. Delete snmp client:
+curl -k -H "X-Auth-Token: $token" -X DELETE https://${bmc}/redfish/v1/EventService/Subscriptions/snmp1
+{
+ "@Message.ExtendedInfo": [
+ {
+ "@odata.type": "#Message.v1_1_1.Message",
+ "Message": "Successfully Completed Request",
+ "MessageArgs": [],
+ "MessageId": "Base.1.8.1.Success",
+ "MessageSeverity": "OK",
+ "Resolution": "None"
+ }
+ ]
+}
+4. After we have added some SNMP clients using Redfish, we can see them in Dbus
+busctl tree xyz.openbmc_project.Network.SNMP
+`-/xyz
+ `-/xyz/openbmc_project
+ `-/xyz/openbmc_project/network
+ `-/xyz/openbmc_project/network/snmp
+ `-/xyz/openbmc_project/network/snmp/manager
+ |-/xyz/openbmc_project/network/snmp/manager/1
+
+busctl introspect xyz.openbmc_project.Network.SNMP
+/xyz/openbmc_project/network/snmp/manager/1 xyz.openbmc_project.Network.Client
+NAME TYPE SIGNATURE RESULT/VALUE FLAGS
+.Address property s "192.168.31.89" emits-change writable
+.Port property q 162 emits-change writable
+
+5. Use "busctl call" add client
+busctl call xyz.openbmc_project.Network.SNMP /xyz/openbmc_project/network/snmp/manager xyz.openbmc_project.Network.Client.Create Client sq 192.168.31.90 162
+s "/xyz/openbmc_project/network/snmp/manager/2"
+
+We will see it use the redfish url:
+curl -k -H "X-Auth-Token: $token" -X GET https://${bmc}/redfish/v1/EventService/Subscriptions/snmp2
+{
+ "@odata.id": "/redfish/v1/EventService/Subscriptions/snmp2",
+ "@odata.type": "#EventDestination.v1_7_0.EventDestination",
+ "Context": "",
+ "Destination": "snmp://192.168.31.90:162",
+ "EventFormatType": "Event",
+ "Id": "snmp2",
+ "Name": "Event Destination snmp2",
+ "Protocol": "SNMPv2c",
+ "SubscriptionType": "SNMPTrap"
+}
+
+6. Deleting snmp client using "busctl"
+First, we use redfish to add some SNMP clients:
+ curl -k -H "X-Auth-Token: $token" -X POST https://${bmc}/redfish/v1/EventService/Subscriptions -d '{"Destination": "snmp://192.168.31.90:162", "SubscriptionType": "SNMPTrap", "Protocol": "SNMPv2c", "Context": "testContext0"}'
+ curl -k -H "X-Auth-Token: $token" -X POST https://${bmc}/redfish/v1/EventService/Subscriptions -d '{"Destination": "snmp://192.168.31.91:162", "SubscriptionType": "SNMPTrap", "Protocol": "SNMPv2c", "Context": "testContext1"}'
+
+Then we can use redfish to get the subscriptions:
+ curl -k -H "X-Auth-Token: $token" -XGET https://${bmc}/redfish/v1/EventService/Subscriptions
+ {
+ "@odata.id": "/redfish/v1/EventService/Subscriptions",
+ "@odata.type": "#EventDestinationCollection.EventDestinationCollection",
+ "Members": [
+ {
+ "@odata.id": "/redfish/v1/EventService/Subscriptions/snmp1"
+ },
+ {
+ "@odata.id": "/redfish/v1/EventService/Subscriptions/snmp2"
+ }
+ ],
+ "Members@odata.count": 2,
+ "Name": "Event Destination Collections"
+ }
+
+Now we use busctl to delete SNMP client 2:
+ busctl call xyz.openbmc_project.Network.SNMP /xyz/openbmc_project/network/snmp/manager/2 xyz.openbmc_project.Object.Delete Delete
+
+Then we won't see snmp2 in the subscriptions of redfish:
+ curl -k -H "X-Auth-Token: $token" -XGET https://${bmc}/redfish/v1/EventService/Subscriptions
+ {
+ "@odata.id": "/redfish/v1/EventService/Subscriptions",
+ "@odata.type": "#EventDestinationCollection.EventDestinationCollection",
+ "Members": [
+ {
+ "@odata.id": "/redfish/v1/EventService/Subscriptions/snmp1"
+ }
+ ],
+ "Members@odata.count": 1,
+ "Name": "Event Destination Collections"
+ }
+---
+ http/utility.hpp | 8 +
+ .../include/event_service_manager.hpp | 18 +-
+ redfish-core/lib/event_service.hpp | 397 +++++++++++++++++-
+ 3 files changed, 399 insertions(+), 24 deletions(-)
+
+diff --git a/http/utility.hpp b/http/utility.hpp
+index 78570169..e9ab786e 100644
+--- a/http/utility.hpp
++++ b/http/utility.hpp
+@@ -723,6 +723,10 @@ inline std::string setProtocolDefaults(const boost::urls::url_view& url)
+ }
+ return "";
+ }
++ if (url.scheme() == "snmp")
++ {
++ return "snmp";
++ }
+ return "";
+ }
+
+@@ -745,6 +749,10 @@ inline uint16_t setPortDefaults(const boost::urls::url_view& url)
+ {
+ return 443;
+ }
++ if (url.scheme() == "snmp")
++ {
++ return 162;
++ }
+ return 0;
+ }
+
+diff --git a/redfish-core/include/event_service_manager.hpp b/redfish-core/include/event_service_manager.hpp
+index b20946a6..636ab5be 100644
+--- a/redfish-core/include/event_service_manager.hpp
++++ b/redfish-core/include/event_service_manager.hpp
+@@ -880,6 +880,7 @@ class EventServiceManager
+ }
+
+ std::string addSubscription(const std::shared_ptr<Subscription>& subValue,
++ const std::string& subscriptionId,
+ const bool updateFile = true)
+ {
+
+@@ -891,12 +892,21 @@ class EventServiceManager
+ int retry = 3;
+ while (retry != 0)
+ {
+- id = std::to_string(dist(gen));
+- if (gen.error())
++ if (subscriptionId.empty())
+ {
+- retry = 0;
+- break;
++ id = std::to_string(dist(gen));
++ if (gen.error())
++ {
++ retry = 0;
++ break;
++ }
++ }
++ else
++ {
++
++ id = subscriptionId;
+ }
++
+ auto inserted = subscriptionsMap.insert(std::pair(id, subValue));
+ if (inserted.second)
+ {
+diff --git a/redfish-core/lib/event_service.hpp b/redfish-core/lib/event_service.hpp
+index 29175abe..bf3bb53a 100644
+--- a/redfish-core/lib/event_service.hpp
++++ b/redfish-core/lib/event_service.hpp
+@@ -22,7 +22,9 @@
+ #include <logging.hpp>
+ #include <query.hpp>
+ #include <registries/privilege_registry.hpp>
++#include <utils/dbus_utils.hpp>
+
++#include <charconv>
+ #include <span>
+
+ namespace redfish
+@@ -45,6 +47,250 @@ static constexpr const std::array<const char*, 1> supportedResourceTypes = {
+
+ static constexpr const uint8_t maxNoOfSubscriptions = 20;
+
++inline void
++ doGetSnmpTrapClientdata(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
++ const std::string& objectPath)
++{
++
++ sdbusplus::asio::getAllProperties(
++ *crow::connections::systemBus,
++ "xyz.openbmc_project.Network.SNMP", objectPath,
++ "xyz.openbmc_project.Network.Client",
++ [asyncResp](const boost::system::error_code ec,
++ const dbus::utility::DBusPropertiesMap& propertiesList) {
++ if (ec)
++ {
++ BMCWEB_LOG_ERROR << "D-Bus response error on GetSubTree " << ec;
++ messages::internalError(asyncResp->res);
++ return;
++ }
++
++ const std::string* address = nullptr;
++ const uint16_t* port = nullptr;
++
++ const bool success = sdbusplus::unpackPropertiesNoThrow(
++ dbus_utils::UnpackErrorPrinter(), propertiesList, "Address",
++ address, "Port", port);
++
++ if (!success)
++ {
++ messages::internalError(asyncResp->res);
++ return;
++ }
++
++ if (address != nullptr && port != nullptr)
++ {
++ std::string destination = "snmp://";
++ destination.append(*address);
++ destination.append(":");
++ destination.append(std::to_string(*port));
++
++ asyncResp->res.jsonValue["Destination"] = std::move(destination);
++ }
++ }
++ );
++}
++
++inline void
++ getSnmpTrapClientdata(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
++ const std::string& id, const std::string& objectPath)
++{
++ asyncResp->res.jsonValue["@odata.type"] =
++ "#EventDestination.v1_7_0.EventDestination";
++ asyncResp->res.jsonValue["Protocol"] = "SNMPv2c";
++ asyncResp->res.jsonValue["@odata.id"] = crow::utility::urlFromPieces(
++ "redfish", "v1", "EventService", "Subscriptions", id);
++
++ asyncResp->res.jsonValue["Id"] = id;
++ asyncResp->res.jsonValue["Name"] = "Event Destination " + id;
++
++ asyncResp->res.jsonValue["SubscriptionType"] = "SNMPTrap";
++ asyncResp->res.jsonValue["EventFormatType"] = "Event";
++
++ std::shared_ptr<Subscription> subValue =
++ EventServiceManager::getInstance().getSubscription(id);
++ if (subValue != nullptr)
++ {
++ asyncResp->res.jsonValue["Context"] = subValue->customText;
++ }
++ else
++ {
++ asyncResp->res.jsonValue["Context"] = "";
++ }
++
++ doGetSnmpTrapClientdata(asyncResp, objectPath);
++}
++
++inline void
++ getSnmpTrapClient(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
++ const std::string& id)
++{
++ crow::connections::systemBus->async_method_call(
++ [asyncResp, id](const boost::system::error_code ec,
++ dbus::utility::ManagedObjectType& resp) {
++ if (ec)
++ {
++ BMCWEB_LOG_ERROR << "D-Bus response error on GetManagedObjects "
++ << ec;
++ messages::internalError(asyncResp->res);
++ return;
++ }
++
++ for (const auto& objpath : resp)
++ {
++ sdbusplus::message::object_path path(objpath.first);
++ const std::string snmpId = path.filename();
++ if (snmpId.empty())
++ {
++ BMCWEB_LOG_ERROR << "The SNMP client ID is wrong";
++ messages::internalError(asyncResp->res);
++ return;
++ }
++ const std::string subscriptionId = "snmp" + snmpId;
++ if (id != subscriptionId)
++ {
++ continue;
++ }
++
++ getSnmpTrapClientdata(asyncResp, id, objpath.first);
++ return;
++ }
++
++ messages::resourceNotFound(asyncResp->res, "Subscriptions", id);
++ EventServiceManager::getInstance().deleteSubscription(id);
++ },
++ "xyz.openbmc_project.Network.SNMP",
++ "/xyz/openbmc_project/network/snmp/manager",
++ "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
++}
++
++inline void
++ createSnmpTrapClient(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
++ const std::string& host, const uint16_t& snmpTrapPort,
++ const std::shared_ptr<Subscription>& subValue)
++{
++ crow::connections::systemBus->async_method_call(
++ [asyncResp, subValue](const boost::system::error_code ec,
++ const std::string& dbusSNMPid) {
++ if (ec)
++ {
++ messages::internalError(asyncResp->res);
++ return;
++ }
++
++ sdbusplus::message::object_path path(dbusSNMPid);
++ const std::string snmpId = path.filename();
++ if (snmpId.empty())
++ {
++ messages::internalError(asyncResp->res);
++ return;
++ }
++
++ const std::string subscriptionId = "snmp" + snmpId;
++
++ std::string id = EventServiceManager::getInstance().addSubscription(
++ subValue, subscriptionId);
++
++ if (id.empty())
++ {
++ messages::internalError(asyncResp->res);
++ return;
++ }
++
++ asyncResp->res.addHeader("Location",
++ "/redfish/v1/EventService/Subscriptions/" +
++ subscriptionId);
++ messages::created(asyncResp->res);
++ },
++ "xyz.openbmc_project.Network.SNMP",
++ "/xyz/openbmc_project/network/snmp/manager",
++ "xyz.openbmc_project.Network.Client.Create", "Client", host,
++ snmpTrapPort);
++}
++inline void
++ checkSnmpTrapClient(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
++ const std::string& host, const uint16_t& snmpTrapPort,
++ const std::string& destUrl,
++ const std::shared_ptr<Subscription>& subValue)
++{
++ crow::connections::systemBus->async_method_call(
++ [asyncResp, host, snmpTrapPort, destUrl,
++ subValue](const boost::system::error_code ec,
++ dbus::utility::ManagedObjectType& resp) {
++ if (ec)
++ {
++ BMCWEB_LOG_ERROR << "D-Bus response error on GetManagedObjects "
++ << ec;
++ messages::internalError(asyncResp->res);
++ return;
++ }
++
++ for (const auto& object : resp)
++ {
++ for (const auto& interface : object.second)
++ {
++ if (interface.first == "xyz.openbmc_project.Network.Client")
++ {
++ std::string address;
++ uint16_t portNum = 0;
++ for (const auto& property : interface.second)
++ {
++ if (property.first == "Address")
++ {
++ const std::string* value =
++ std::get_if<std::string>(&property.second);
++ if (value == nullptr)
++ {
++ continue;
++ }
++ address = *value;
++ }
++ else if (property.first == "Port")
++ {
++ const uint16_t* value =
++ std::get_if<uint16_t>(&property.second);
++ if (value == nullptr)
++ {
++ continue;
++ }
++ portNum = *value;
++ }
++ }
++
++ if (address == host && portNum == snmpTrapPort)
++ {
++ messages::resourceAlreadyExists(
++ asyncResp->res,
++ "EventDestination.v1_7_0.EventDestination",
++ "Destination", destUrl);
++ return;
++ }
++ }
++ }
++ }
++ // Create the snmp client
++ createSnmpTrapClient(asyncResp, host, snmpTrapPort, subValue);
++ },
++ "xyz.openbmc_project.Network.SNMP",
++ "/xyz/openbmc_project/network/snmp/manager",
++ "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
++}
++
++inline void
++ getSnmpSubscriptionList(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
++ const std::string& snmpId,
++ nlohmann::json& memberArray)
++{
++ const std::string subscriptionId = "snmp" + snmpId;
++
++ nlohmann::json::object_t member;
++ member["@odata.id"] = crow::utility::urlFromPieces(
++ "redfish", "v1", "EventService", "Subscriptions", subscriptionId);
++ memberArray.push_back(std::move(member));
++
++ asyncResp->res.jsonValue["Members@odata.count"] = memberArray.size();
++}
++
+ inline void requestRoutesEventService(App& app)
+ {
+ BMCWEB_ROUTE(app, "/redfish/v1/EventService/")
+@@ -180,6 +426,59 @@ inline void requestRoutesSubmitTestEvent(App& app)
+ });
+ }
+
++inline void doSubscriptionCollection(
++ const boost::system::error_code ec,
++ const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
++ dbus::utility::ManagedObjectType& resp)
++{
++ nlohmann::json& memberArray = asyncResp->res.jsonValue["Members"];
++ std::vector<std::string> subscripIds =
++ EventServiceManager::getInstance().getAllIDs();
++ memberArray = nlohmann::json::array();
++
++ for (const std::string& id : subscripIds)
++ {
++ if (!boost::starts_with(id, "snmp"))
++ {
++ nlohmann::json::object_t member;
++ member["@odata.id"] =
++ "/redfish/v1/EventService/Subscriptions/" + id;
++ memberArray.push_back(std::move(member));
++ }
++ }
++
++ asyncResp->res.jsonValue["Members@odata.count"] = memberArray.size();
++
++ if (ec)
++ {
++ if (ec.value() == EBADR)
++ {
++ // this is an optional process so just return if
++ // it isn't there
++ return;
++ }
++
++ BMCWEB_LOG_ERROR << "D-Bus response error on GetManagedObjects " << ec;
++ messages::internalError(asyncResp->res);
++ return;
++ }
++
++ for (const std::pair<sdbusplus::message::object_path,
++ dbus::utility::DBusInteracesMap>& objpath : resp)
++ {
++ sdbusplus::message::object_path path(objpath.first);
++ const std::string snmpId = path.filename();
++ if (snmpId.empty())
++ {
++ BMCWEB_LOG_ERROR << "The SNMP client ID is wrong";
++ messages::internalError(asyncResp->res);
++ return;
++ }
++
++ getSnmpSubscriptionList(asyncResp, snmpId, memberArray);
++ }
++}
++
+ inline void requestRoutesEventDestinationCollection(App& app)
+ {
+ BMCWEB_ROUTE(app, "/redfish/v1/EventService/Subscriptions/")
+@@ -197,21 +496,16 @@ inline void requestRoutesEventDestinationCollection(App& app)
+ "/redfish/v1/EventService/Subscriptions";
+ asyncResp->res.jsonValue["Name"] = "Event Destination Collections";
+
+- nlohmann::json& memberArray = asyncResp->res.jsonValue["Members"];
+-
+- std::vector<std::string> subscripIds =
+- EventServiceManager::getInstance().getAllIDs();
+- memberArray = nlohmann::json::array();
+- asyncResp->res.jsonValue["Members@odata.count"] = subscripIds.size();
+-
+- for (const std::string& id : subscripIds)
+- {
+- nlohmann::json::object_t member;
+- member["@odata.id"] =
+- "/redfish/v1/EventService/Subscriptions/" + id;
+- memberArray.push_back(std::move(member));
+- }
++ crow::connections::systemBus->async_method_call(
++ [asyncResp](const boost::system::error_code ec,
++ dbus::utility::ManagedObjectType& resp) {
++ doSubscriptionCollection(ec, asyncResp, resp);
++ },
++ "xyz.openbmc_project.Network.SNMP",
++ "/xyz/openbmc_project/network/snmp/manager",
++ "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
+ });
++
+ BMCWEB_ROUTE(app, "/redfish/v1/EventService/Subscriptions/")
+ .privileges(redfish::privileges::postEventDestinationCollection)
+ .methods(boost::beast::http::verb::post)(
+@@ -286,7 +580,8 @@ inline void requestRoutesEventDestinationCollection(App& app)
+
+ if (subscriptionType)
+ {
+- if (*subscriptionType != "RedfishEvent")
++ if (*subscriptionType != "RedfishEvent" &&
++ (*subscriptionType != "SNMPTrap"))
+ {
+ messages::propertyValueNotInList(
+ asyncResp->res, *subscriptionType, "SubscriptionType");
+@@ -296,10 +591,17 @@ inline void requestRoutesEventDestinationCollection(App& app)
+ }
+ else
+ {
+- subValue->subscriptionType = "RedfishEvent"; // Default
++ if (protocol == "SNMPv2c")
++ {
++ subValue->subscriptionType = "SNMPTrap";
++ }
++ else
++ {
++ subValue->subscriptionType = "RedfishEvent"; // Default
++ }
+ }
+
+- if (protocol != "Redfish")
++ if ((protocol != "Redfish") && (protocol != "SNMPv2c"))
+ {
+ messages::propertyValueNotInList(asyncResp->res, protocol,
+ "Protocol");
+@@ -466,8 +768,18 @@ inline void requestRoutesEventDestinationCollection(App& app)
+ }
+ }
+
+- std::string id =
+- EventServiceManager::getInstance().addSubscription(subValue);
++ if (protocol == "SNMPv2c")
++ {
++ // Check whether the client already exists
++ checkSnmpTrapClient(asyncResp, host, port, destUrl, subValue);
++ return;
++ }
++
++ // There is no need to set the default subscriptionId here.
++ const std::string subscriptionId;
++
++ std::string id = EventServiceManager::getInstance().addSubscription(
++ subValue, subscriptionId);
+ if (id.empty())
+ {
+ messages::internalError(asyncResp->res);
+@@ -492,6 +804,13 @@ inline void requestRoutesEventDestination(App& app)
+ {
+ return;
+ }
++
++ if (boost::starts_with(param, "snmp"))
++ {
++ getSnmpTrapClient(asyncResp, param);
++ return;
++ }
++
+ std::shared_ptr<Subscription> subValue =
+ EventServiceManager::getInstance().getSubscription(param);
+ if (subValue == nullptr)
+@@ -503,7 +822,7 @@ inline void requestRoutesEventDestination(App& app)
+
+ asyncResp->res.jsonValue["@odata.type"] =
+ "#EventDestination.v1_7_0.EventDestination";
+- asyncResp->res.jsonValue["Protocol"] = "Redfish";
++ asyncResp->res.jsonValue["Protocol"] = subValue->protocol;
+ asyncResp->res.jsonValue["@odata.id"] =
+ "/redfish/v1/EventService/Subscriptions/" + id;
+ asyncResp->res.jsonValue["Id"] = id;
+@@ -620,6 +939,44 @@ inline void requestRoutesEventDestination(App& app)
+ {
+ return;
+ }
++
++ if (boost::starts_with(param, "snmp"))
++ {
++ std::string_view snmpTrapId = param;
++
++ // Erase "snmp" in the request to find the corresponding
++ // dbus snmp client id. For example, the snmpid in the
++ // request is "snmp1", which will be "1" after being erased.
++ snmpTrapId.remove_prefix(4);
++
++ const std::string snmpPath =
++ "/xyz/openbmc_project/network/snmp/manager/" +
++ std::string(snmpTrapId);
++
++ crow::connections::systemBus->async_method_call(
++ [asyncResp, param](const boost::system::error_code ec) {
++ if (ec)
++ {
++ // The snmp trap id is incorrect
++ if (ec.value() == EBADR)
++ {
++ messages::resourceNotFound(asyncResp->res,
++ "Subscription", param);
++ return;
++ }
++ messages::internalError(asyncResp->res);
++ return;
++ }
++ messages::success(asyncResp->res);
++ },
++ "xyz.openbmc_project.Network.SNMP", snmpPath,
++ "xyz.openbmc_project.Object.Delete", "Delete");
++
++ EventServiceManager::getInstance().deleteSubscription(param);
++
++ return;
++ }
++
+ if (!EventServiceManager::getInstance().isSubscriptionExist(param))
+ {
+ asyncResp->res.result(boost::beast::http::status::not_found);
+--
+2.35.1
+
diff --git a/meta-ibs/meta-cp2-5422/recipes-phosphor/interfaces/bmcweb_%.bbappend b/meta-ibs/meta-cp2-5422/recipes-phosphor/interfaces/bmcweb_%.bbappend
index 55e0ca2b60..72aa017f07 100644
--- a/meta-ibs/meta-cp2-5422/recipes-phosphor/interfaces/bmcweb_%.bbappend
+++ b/meta-ibs/meta-cp2-5422/recipes-phosphor/interfaces/bmcweb_%.bbappend
@@ -11,6 +11,7 @@ SRC_URI += "\
file://0012-add-telemetry-hour-data.patch \
file://0013-bugfix-telemetry-circular-buffer.patch \
file://0014-Additional-details-about-errors-when-change-user-pw.patch \
+ file://0015-Redfish-Implement-SNMP-Trap.patch \
"
#SRC_URI += "\