diff options
author | AppaRao Puli <apparao.puli@linux.intel.com> | 2020-04-25 03:34:05 +0300 |
---|---|---|
committer | AppaRao Puli <apparao.puli@linux.intel.com> | 2020-05-14 04:09:21 +0300 |
commit | 156d6b00a9dc06ac9f618ecd8d5fc6b1b49d5be4 (patch) | |
tree | 874fd69b33be5f9da033b10b01b340b74595b1e2 /redfish-core | |
parent | e46946ac9e5c72dbd63d36e420dba417243346bb (diff) | |
download | bmcweb-156d6b00a9dc06ac9f618ecd8d5fc6b1b49d5be4.tar.xz |
EventService: Add MetricReport support
Add Telemetry metric report support to EventService.
- Adding MetricReport support to schema implemenation.
- Dynamically register and unregister the metric report
signal.
- Reads Telemtry data using D-Bus calls.
- Filter the metric reports depending on user configured
MetricReportDefinition.
- Format the Telemetry readings as per MetricReport schema.
- Send the formatted data to the client.
Tested:
- HTTP client successfully received asynchronous metric
reports data.
- valdiated the register and unregister by adding and
deleting subscriptions.
- Ran Redfish validator successfully.
Change-Id: I7b59ac3ecad169a7959a800730dbc2fe85baf068
Signed-off-by: AppaRao Puli <apparao.puli@linux.intel.com>
Diffstat (limited to 'redfish-core')
-rw-r--r-- | redfish-core/include/event_service_manager.hpp | 175 | ||||
-rw-r--r-- | redfish-core/lib/event_service.hpp | 15 |
2 files changed, 186 insertions, 4 deletions
diff --git a/redfish-core/include/event_service_manager.hpp b/redfish-core/include/event_service_manager.hpp index 553fc5f46f..297e141099 100644 --- a/redfish-core/include/event_service_manager.hpp +++ b/redfish-core/include/event_service_manager.hpp @@ -32,6 +32,13 @@ namespace redfish { + +using ReadingsObjType = + std::vector<std::tuple<std::string, std::string, double, std::string>>; + +static constexpr const char* eventFormatType = "Event"; +static constexpr const char* metricReportFormatType = "MetricReport"; + #ifndef BMCWEB_ENABLE_REDFISH_DBUS_LOG_ENTRIES constexpr const char* redfishEventLogFile = "/var/log/redfish"; constexpr const uint32_t inotifyFileAction = IN_MODIFY; @@ -263,6 +270,7 @@ class Subscription std::vector<std::string> registryMsgIds; std::vector<std::string> registryPrefixes; std::vector<nlohmann::json> httpHeaders; // key-value pair + std::vector<nlohmann::json> metricReportDefinitions; Subscription(const Subscription&) = delete; Subscription& operator=(const Subscription&) = delete; @@ -387,6 +395,48 @@ class Subscription } #endif + void filterAndSendReports(const std::string& id, + const std::string& readingsTs, + const ReadingsObjType& readings) + { + std::string metricReportDef = + "/redfish/v1/TelemetryService/MetricReportDefinitions/" + id; + + // Empty list means no filter. Send everything. + if (metricReportDefinitions.size()) + { + if (std::find(metricReportDefinitions.begin(), + metricReportDefinitions.end(), + metricReportDef) == metricReportDefinitions.end()) + { + return; + } + } + + nlohmann::json metricValuesArray = nlohmann::json::array(); + for (const auto& it : readings) + { + metricValuesArray.push_back({}); + nlohmann::json& entry = metricValuesArray.back(); + + entry = {{"MetricId", std::get<0>(it)}, + {"MetricProperty", std::get<1>(it)}, + {"MetricValue", std::to_string(std::get<2>(it))}, + {"Timestamp", std::get<3>(it)}}; + } + + nlohmann::json msg = { + {"@odata.id", "/redfish/v1/TelemetryService/MetricReports/" + id}, + {"@odata.type", "#MetricReport.v1_3_0.MetricReport"}, + {"Id", id}, + {"Name", id}, + {"Timestamp", readingsTs}, + {"MetricReportDefinition", {{"@odata.id", metricReportDef}}}, + {"MetricValues", metricValuesArray}}; + + this->sendEvent(msg.dump()); + } + private: uint64_t eventSeqNum; std::string host; @@ -404,7 +454,7 @@ class EventServiceManager EventServiceManager(EventServiceManager&&) = delete; EventServiceManager& operator=(EventServiceManager&&) = delete; - EventServiceManager() + EventServiceManager() : noOfMetricReportSubscribers(0) { // TODO: Read the persistent data from store and populate. // Populating with default. @@ -414,6 +464,8 @@ class EventServiceManager } std::string lastEventTStr; + size_t noOfMetricReportSubscribers; + std::shared_ptr<sdbusplus::bus::match::match> matchTelemetryMonitor; boost::container::flat_map<std::string, std::shared_ptr<Subscription>> subscriptionsMap; @@ -471,6 +523,15 @@ class EventServiceManager return std::string(""); } + if (subValue->eventFormatType == metricReportFormatType) + { + // If it is first entry, Register Metrics report signal. + if ((++noOfMetricReportSubscribers == 1)) + { + registerMetricReportSignal(); + } + } + updateSubscriptionData(); #ifndef BMCWEB_ENABLE_REDFISH_DBUS_LOG_ENTRIES @@ -497,6 +558,15 @@ class EventServiceManager auto obj = subscriptionsMap.find(id); if (obj != subscriptionsMap.end()) { + std::shared_ptr<Subscription> entry = obj->second; + if (entry->eventFormatType == metricReportFormatType) + { + if ((--noOfMetricReportSubscribers == 0)) + { + unregisterMetricReportSignal(); + } + } + subscriptionsMap.erase(obj); updateSubscriptionData(); } @@ -699,6 +769,109 @@ class EventServiceManager } #endif + + void getMetricReading(const std::string& service, + const std::string& objPath, const std::string& intf) + { + std::size_t found = objPath.find_last_of("/"); + if (found == std::string::npos) + { + BMCWEB_LOG_DEBUG << "Invalid objPath received"; + return; + } + + std::string idStr = objPath.substr(found + 1); + if (idStr.empty()) + { + BMCWEB_LOG_DEBUG << "Invalid ID in objPath"; + return; + } + + crow::connections::systemBus->async_method_call( + [idStr{std::move(idStr)}]( + const boost::system::error_code ec, + boost::container::flat_map< + std::string, std::variant<std::string, ReadingsObjType>>& + resp) { + if (ec) + { + BMCWEB_LOG_DEBUG + << "D-Bus call failed to GetAll metric readings."; + return; + } + + const std::string* timestampPtr = + std::get_if<std::string>(&resp["Timestamp"]); + if (!timestampPtr) + { + BMCWEB_LOG_DEBUG << "Failed to Get timestamp."; + return; + } + + ReadingsObjType* readingsPtr = + std::get_if<ReadingsObjType>(&resp["Readings"]); + if (!readingsPtr) + { + BMCWEB_LOG_DEBUG << "Failed to Get Readings property."; + return; + } + + if (!readingsPtr->size()) + { + BMCWEB_LOG_DEBUG << "No metrics report to be transferred"; + return; + } + + for (const auto& it : + EventServiceManager::getInstance().subscriptionsMap) + { + std::shared_ptr<Subscription> entry = it.second; + if (entry->eventFormatType == metricReportFormatType) + { + entry->filterAndSendReports(idStr, *timestampPtr, + *readingsPtr); + } + } + }, + service, objPath, "org.freedesktop.DBus.Properties", "GetAll", + intf); + } + + void unregisterMetricReportSignal() + { + BMCWEB_LOG_DEBUG << "Metrics report signal - Unregister"; + matchTelemetryMonitor.reset(); + matchTelemetryMonitor = nullptr; + } + + void registerMetricReportSignal() + { + if (matchTelemetryMonitor) + { + BMCWEB_LOG_DEBUG << "Metrics report signal - Already registered."; + return; + } + + BMCWEB_LOG_DEBUG << "Metrics report signal - Register"; + std::string matchStr( + "type='signal',member='ReportUpdate', " + "interface='xyz.openbmc_project.MonitoringService.Report'"); + + matchTelemetryMonitor = std::make_shared<sdbusplus::bus::match::match>( + *crow::connections::systemBus, matchStr, + [this](sdbusplus::message::message& msg) { + if (msg.is_method_error()) + { + BMCWEB_LOG_ERROR << "TelemetryMonitor Signal error"; + return; + } + + std::string service = msg.get_sender(); + std::string objPath = msg.get_path(); + std::string intf = msg.get_interface(); + getMetricReading(service, objPath, intf); + }); + } }; } // namespace redfish diff --git a/redfish-core/lib/event_service.hpp b/redfish-core/lib/event_service.hpp index 3e3435ff49..63302d6072 100644 --- a/redfish-core/lib/event_service.hpp +++ b/redfish-core/lib/event_service.hpp @@ -19,8 +19,8 @@ namespace redfish { -static constexpr const std::array<const char*, 1> supportedEvtFormatTypes = { - "Event"}; +static constexpr const std::array<const char*, 2> supportedEvtFormatTypes = { + eventFormatType, metricReportFormatType}; static constexpr const std::array<const char*, 3> supportedRegPrefixes = { "Base", "OpenBMC", "Task"}; static constexpr const std::array<const char*, 3> supportedRetryPolicies = { @@ -217,13 +217,15 @@ class EventDestinationCollection : public Node std::optional<std::vector<std::string>> msgIds; std::optional<std::vector<std::string>> regPrefixes; std::optional<std::vector<nlohmann::json>> headers; + std::optional<std::vector<nlohmann::json>> metricReportDefinitions; if (!json_util::readJson( req, res, "Destination", destUrl, "Context", context, "Protocol", protocol, "SubscriptionType", subscriptionType, "EventFormatType", eventFormatType, "HttpHeaders", headers, "RegistryPrefixes", regPrefixes, "MessageIds", msgIds, - "DeliveryRetryPolicy", retryPolicy)) + "DeliveryRetryPolicy", retryPolicy, "MetricReportDefinitions", + metricReportDefinitions)) { return; } @@ -373,6 +375,11 @@ class EventDestinationCollection : public Node subValue->retryPolicy = "TerminateAfterRetries"; } + if (metricReportDefinitions) + { + subValue->metricReportDefinitions = *metricReportDefinitions; + } + std::string id = EventServiceManager::getInstance().addSubscription(subValue); if (id.empty()) @@ -441,6 +448,8 @@ class EventDestination : public Node subValue->registryPrefixes; asyncResp->res.jsonValue["MessageIds"] = subValue->registryMsgIds; asyncResp->res.jsonValue["DeliveryRetryPolicy"] = subValue->retryPolicy; + asyncResp->res.jsonValue["MetricReportDefinitions"] = + subValue->metricReportDefinitions; } void doPatch(crow::Response& res, const crow::Request& req, |