diff options
-rw-r--r-- | http/utility.hpp | 8 | ||||
-rw-r--r-- | redfish-core/include/snmp_trap_event_clients.hpp | 232 | ||||
-rw-r--r-- | redfish-core/lib/event_service.hpp | 126 |
3 files changed, 363 insertions, 3 deletions
diff --git a/http/utility.hpp b/http/utility.hpp index aa64043132..3fbdd0c32f 100644 --- a/http/utility.hpp +++ b/http/utility.hpp @@ -551,6 +551,10 @@ inline std::string setProtocolDefaults(boost::urls::url_view urlView) } return ""; } + if (urlView.scheme() == "snmp") + { + return "snmp"; + } return ""; } @@ -573,6 +577,10 @@ inline uint16_t setPortDefaults(boost::urls::url_view url) { return 443; } + if (url.scheme() == "snmp") + { + return 162; + } return 0; } diff --git a/redfish-core/include/snmp_trap_event_clients.hpp b/redfish-core/include/snmp_trap_event_clients.hpp new file mode 100644 index 0000000000..08ff81b439 --- /dev/null +++ b/redfish-core/include/snmp_trap_event_clients.hpp @@ -0,0 +1,232 @@ +#pragma once + +#include "async_resp.hpp" +#include "dbus_singleton.hpp" +#include "dbus_utility.hpp" +#include "error_messages.hpp" +#include "event_service_manager.hpp" +#include "http_request.hpp" +#include "http_response.hpp" +#include "logging.hpp" +#include "utils/dbus_utils.hpp" + +#include <boost/system/error_code.hpp> +#include <boost/url/format.hpp> +#include <sdbusplus/unpack_properties.hpp> + +#include <memory> +#include <string> +#include <string_view> + +namespace redfish +{ + +inline void afterGetSnmpTrapClientdata( + const std::shared_ptr<bmcweb::AsyncResp>& 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; + } + + std::string address; + uint16_t port = 0; + + bool success = sdbusplus::unpackPropertiesNoThrow( + dbus_utils::UnpackErrorPrinter(), propertiesList, "Address", address, + "Port", port); + + if (!success) + { + messages::internalError(asyncResp->res); + return; + } + + asyncResp->res.jsonValue["Destination"] = + boost::urls::format("snmp://{}:{}", address, port); +} + +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_8_0.EventDestination"; + asyncResp->res.jsonValue["Protocol"] = "SNMPv2c"; + asyncResp->res.jsonValue["@odata.id"] = + boost::urls::format("/redfish/v1/EventService/Subscriptions/{}", id); + + asyncResp->res.jsonValue["Id"] = id; + asyncResp->res.jsonValue["Name"] = "Event Destination"; + + 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"] = ""; + } + + 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& properties) { + afterGetSnmpTrapClientdata(asyncResp, ec, properties); + }); +} + +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 + afterSnmpClientCreate(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, + const boost::system::error_code& ec, + const std::string& dbusSNMPid) +{ + if (ec) + { + if (ec.value() != EBADR) + { + // SNMP not installed + messages::propertyValueOutOfRange(asyncResp->res, "SNMPv2c", + "Protocol"); + return; + } + 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; + } + + std::string subscriptionId = "snmp" + snmpId; + + boost::urls::url uri = boost::urls::format( + "/redfish/v1/EventService/Subscriptions/{}", subscriptionId); + asyncResp->res.addHeader("Location", uri.buffer()); + messages::created(asyncResp->res); +} + +inline void + addSnmpTrapClient(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, + const std::string& host, uint16_t snmpTrapPort) +{ + crow::connections::systemBus->async_method_call( + [asyncResp](const boost::system::error_code& ec, + const std::string& dbusSNMPid) { + afterSnmpClientCreate(asyncResp, ec, dbusSNMPid); + }, + "xyz.openbmc_project.Network.SNMP", + "/xyz/openbmc_project/network/snmp/manager", + "xyz.openbmc_project.Network.Client.Create", "Client", host, + snmpTrapPort); +} + +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"] = boost::urls::format( + "/redfish/v1/EventService/Subscriptions/{}", subscriptionId); + memberArray.push_back(std::move(member)); + + asyncResp->res.jsonValue["Members@odata.count"] = memberArray.size(); +} + +inline void + deleteSnmpTrapClient(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, + const std::string& param) +{ + 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); + + sdbusplus::message::object_path snmpPath = + sdbusplus::message::object_path( + "/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", static_cast<std::string>(snmpPath), + "xyz.openbmc_project.Object.Delete", "Delete"); +} + +} // namespace redfish diff --git a/redfish-core/lib/event_service.hpp b/redfish-core/lib/event_service.hpp index 28a7ac6b6d..aef4d47278 100644 --- a/redfish-core/lib/event_service.hpp +++ b/redfish-core/lib/event_service.hpp @@ -20,10 +20,17 @@ #include "logging.hpp" #include "query.hpp" #include "registries/privilege_registry.hpp" +#include "snmp_trap_event_clients.hpp" #include <boost/beast/http/fields.hpp> +#include <boost/system/error_code.hpp> +#include <sdbusplus/unpack_properties.hpp> +#include <utils/dbus_utils.hpp> +#include <charconv> +#include <memory> #include <span> +#include <string> namespace redfish { @@ -183,6 +190,39 @@ inline void requestRoutesSubmitTestEvent(App& app) }); } +inline void doSubscriptionCollection( + const boost::system::error_code ec, + const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, + const dbus::utility::ManagedObjectType& resp) +{ + 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; + } + nlohmann::json& memberArray = asyncResp->res.jsonValue["Members"]; + 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; + } + + getSnmpSubscriptionList(asyncResp, snmpId, memberArray); + } +} + inline void requestRoutesEventDestinationCollection(App& app) { BMCWEB_ROUTE(app, "/redfish/v1/EventService/Subscriptions/") @@ -210,11 +250,20 @@ inline void requestRoutesEventDestinationCollection(App& app) for (const std::string& id : subscripIds) { nlohmann::json::object_t member; - member["@odata.id"] = "/redfish/v1/EventService/Subscriptions/" + - id; + member["@odata.id"] = boost::urls::format( + "/redfish/v1/EventService/Subscriptions/{}" + id); memberArray.emplace_back(std::move(member)); } + crow::connections::systemBus->async_method_call( + [asyncResp](const boost::system::error_code ec, + const 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)( @@ -287,10 +336,66 @@ inline void requestRoutesEventDestinationCollection(App& app) return; } + if (protocol == "SNMPv2c") + { + if (context) + { + messages::propertyValueConflict(asyncResp->res, "Context", + "Protocol"); + return; + } + if (eventFormatType2) + { + messages::propertyValueConflict(asyncResp->res, + "EventFormatType", "Protocol"); + return; + } + if (retryPolicy) + { + messages::propertyValueConflict(asyncResp->res, "RetryPolicy", + "Protocol"); + return; + } + if (msgIds) + { + messages::propertyValueConflict(asyncResp->res, "MessageIds", + "Protocol"); + return; + } + if (regPrefixes) + { + messages::propertyValueConflict(asyncResp->res, + "RegistryPrefixes", "Protocol"); + return; + } + if (resTypes) + { + messages::propertyValueConflict(asyncResp->res, "ResourceTypes", + "Protocol"); + return; + } + if (headers) + { + messages::propertyValueConflict(asyncResp->res, "HttpHeaders", + "Protocol"); + return; + } + if (mrdJsonArray) + { + messages::propertyValueConflict( + asyncResp->res, "MetricReportDefinitions", "Protocol"); + return; + } + + addSnmpTrapClient(asyncResp, host, port); + return; + } + if (path.empty()) { path = "/"; } + std::shared_ptr<Subscription> subValue = std::make_shared<Subscription>( host, port, path, urlProto, app.ioContext()); @@ -526,6 +631,13 @@ inline void requestRoutesEventDestination(App& app) { return; } + + if (param.starts_with("snmp")) + { + getSnmpTrapClient(asyncResp, param); + return; + } + std::shared_ptr<Subscription> subValue = EventServiceManager::getInstance().getSubscription(param); if (subValue == nullptr) @@ -536,7 +648,7 @@ inline void requestRoutesEventDestination(App& app) const std::string& id = param; asyncResp->res.jsonValue["@odata.type"] = - "#EventDestination.v1_7_0.EventDestination"; + "#EventDestination.v1_8_0.EventDestination"; asyncResp->res.jsonValue["Protocol"] = "Redfish"; asyncResp->res.jsonValue["@odata.id"] = "/redfish/v1/EventService/Subscriptions/" + id; @@ -653,6 +765,14 @@ inline void requestRoutesEventDestination(App& app) { return; } + + if (param.starts_with("snmp")) + { + deleteSnmpTrapClient(asyncResp, param); + EventServiceManager::getInstance().deleteSubscription(param); + return; + } + if (!EventServiceManager::getInstance().isSubscriptionExist(param)) { asyncResp->res.result(boost::beast::http::status::not_found); |