| |
| #pragma once |
| |
| #include "app.hpp" |
| #include "async_resp.hpp" |
| #include "dbus_utility.hpp" |
| |
| #ifdef BMCWEB_ENABLE_GRPC |
| #include "grpc_statistics.h" |
| #endif |
| |
| #include "http_request.hpp" |
| #include "openbmc_dbus_rest.hpp" |
| #include "privileges.hpp" |
| #include "query.hpp" |
| #include "registries/privilege_registry.hpp" |
| #include "routing.hpp" |
| |
| #include <boost/algorithm/string/predicate.hpp> |
| #include <nlohmann/json.hpp> |
| #include <sdbusplus/asio/property.hpp> |
| |
| #include <string> |
| #include <string_view> |
| #include <unordered_set> |
| |
| namespace redfish |
| { |
| |
| using SensorVariantType = dbus::utility::DbusVariantType; |
| |
| const std::unordered_set<std::string> daemonDoublePropertiesSet{ |
| "KernelTimeSeconds", "UserTimeSeconds", "UptimeSeconds"}; |
| |
| const std::unordered_set<std::string> daemonSizeTPropertiesSet{ |
| "ResidentSetSizeBytes", "NFileDescriptors", "RestartCount"}; |
| |
| constexpr std::array bootupTimeProperties = { |
| "FirmwareTimestampMonotonic", "LoaderTimestampMonotonic", |
| "KernelTimestampMonotonic", "InitRDTimestampMonotonic", |
| "UserspaceTimestampMonotonic", "FinishTimestampMonotonic"}; |
| |
| constexpr std::array bootupTimeMetricName = { |
| "FirmwareTimeSeconds", "LoaderTimeSeconds", "KernelTimeSeconds", |
| "InitrdTimeSeconds", "UserSpaceTimeSeconds"}; |
| |
| std::optional<std::pair<std::string, std::string>> |
| checkAndGetSoleGetSubTreeResponse( |
| const dbus::utility::MapperGetSubTreeResponse& subtree) |
| { |
| if (subtree.empty()) |
| { |
| BMCWEB_LOG_DEBUG << "Can't find bmc D-Bus object!"; |
| return std::nullopt; |
| } |
| |
| // TODO: Consider Multi-BMC cases |
| // Assume only 1 bmc D-Bus object |
| // Throw an error if there is more than 1 |
| if (subtree.size() > 1) |
| { |
| BMCWEB_LOG_DEBUG << "Found more than 1 bmc D-Bus object!"; |
| return std::nullopt; |
| } |
| |
| if (subtree[0].first.empty() || subtree[0].second.size() != 1) |
| { |
| BMCWEB_LOG_DEBUG << "Error getting bmc D-Bus object!"; |
| return std::nullopt; |
| } |
| |
| return std::make_pair(subtree[0].first, subtree[0].second[0].first); |
| } |
| |
| // Returns the sole non-Object Mapper service |
| std::optional<std::string> checkAndGetSoleNonMapperGetObjectResponse( |
| const dbus::utility::MapperGetObject& objects) |
| { |
| if (objects.empty()) |
| { |
| return std::nullopt; |
| } |
| |
| for (const std::pair<std::string, std::vector<std::string>>& |
| serviceAndIfaces : objects) |
| { |
| if (serviceAndIfaces.first != "xyz.openbmc_project.ObjectMapper") |
| { |
| return serviceAndIfaces.first; |
| } |
| } |
| return std::nullopt; |
| } |
| |
| /** |
| * @brief Checks whether the last segment of a DBus object is a specific value. |
| * |
| * @param[i] objectPath - the DBus object path to check |
| * @param[i] value - the value to test against |
| * |
| * @return bool |
| */ |
| bool dbusObjectFilenameEquals(const std::string& objectName, |
| const std::string& value) |
| { |
| sdbusplus::message::object_path objectPath(objectName); |
| return (objectPath.filename() == value); |
| } |
| |
| /** |
| * @brief Populates on entry in ManagerDiagnosticData with one utility sensor |
| * DBus object. |
| * |
| * @param[i,o] asyncResp - Async response object |
| * @param[i] diagnosticDataObjectPath - the DBus object path of the utility |
| * sensor. |
| * |
| * @return void |
| */ |
| inline void populateOneManagerDiagnosticDataEntry( |
| const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, |
| const std::string& diagnosticDataObjectPath) |
| { |
| crow::connections::systemBus->async_method_call( |
| [asyncResp, diagnosticDataObjectPath]( |
| const boost::system::error_code ec, |
| const dbus::utility::MapperGetObject& objects) { |
| if (ec) |
| { |
| BMCWEB_LOG_ERROR << "Error occurred GetObject"; |
| return; |
| } |
| |
| std::optional<std::string> serviceName = |
| checkAndGetSoleNonMapperGetObjectResponse(objects); |
| if (!serviceName) |
| { |
| BMCWEB_LOG_ERROR << "Cannot get service from object name" |
| << diagnosticDataObjectPath; |
| return; |
| } |
| |
| managedStore::ManagedObjectStoreContext context(asyncResp); |
| dbus_utils::getProperty<double>( |
| *serviceName, diagnosticDataObjectPath, |
| "xyz.openbmc_project.Sensor.Value", "Value", context, |
| [asyncResp, diagnosticDataObjectPath]( |
| const boost::system::error_code ec2, const double value) { |
| if (ec2) |
| { |
| BMCWEB_LOG_DEBUG << "DBus response error on getProperty " |
| << ec2; |
| return; |
| } |
| if (dbusObjectFilenameEquals(diagnosticDataObjectPath, |
| "CPU_Kernel")) |
| { |
| asyncResp->res |
| .jsonValue["ProcessorStatistics"]["KernelPercent"] = value; |
| } |
| else if (dbusObjectFilenameEquals(diagnosticDataObjectPath, |
| "CPU_User")) |
| { |
| asyncResp->res.jsonValue["ProcessorStatistics"]["UserPercent"] = |
| value; |
| } |
| else if (dbusObjectFilenameEquals(diagnosticDataObjectPath, |
| "Memory_AvailableBytes")) |
| { |
| asyncResp->res.jsonValue["MemoryStatistics"]["AvailableBytes"] = |
| static_cast<int64_t>(value); |
| } |
| }); |
| }, |
| "xyz.openbmc_project.ObjectMapper", |
| "/xyz/openbmc_project/object_mapper", |
| "xyz.openbmc_project.ObjectMapper", "GetObject", |
| diagnosticDataObjectPath, std::array<const char*, 0>{}); |
| } |
| |
| /** |
| * @brief Find the endpoints for an Association definition. |
| * |
| * @param[i,o] asyncResp - Async response object |
| * @param[i] bmcAssociationObjectPath - the DBus object path of the Association |
| * definition. |
| * @param[i] bmcAssociationService - the DBus service that contains the |
| * Association definition. |
| * |
| * @return void |
| */ |
| inline void findBmcAssociationEndpoints( |
| const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, |
| const std::string& bmcAssociationObjectPath, |
| const std::string& bmcAssociationService) |
| { |
| // No need to use std::variant when using getProperty, just use the |
| // contained type in the template parameter |
| managedStore::ManagedObjectStoreContext context(asyncResp); |
| dbus_utils::getProperty<std::vector<std::string>>( |
| bmcAssociationService, bmcAssociationObjectPath, |
| "xyz.openbmc_project.Association", "endpoints", context, |
| [asyncResp](const boost::system::error_code ec, |
| const std::vector<std::string>& v) { |
| if (ec) |
| { |
| BMCWEB_LOG_ERROR << "DBus error getting association endpoints"; |
| return; |
| } |
| |
| for (const std::string& endpoint : v) |
| { |
| populateOneManagerDiagnosticDataEntry(asyncResp, endpoint); |
| } |
| }); |
| } |
| |
| /** |
| * @brief Find Associations related to one BMC inventory item. |
| * |
| * @param[i,o] asyncResp - Async response object |
| * @param[i] bmcObjectPath - the DBus object path of the BMC inventory item. |
| * |
| * @return void |
| */ |
| inline void |
| findBmcAssociation(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, |
| const std::string& bmcObjectPath) |
| { |
| std::array<std::string_view, 1> interfaces{ |
| "xyz.openbmc_project.Association"}; |
| managedStore::ManagedObjectStoreContext requestContext(asyncResp); |
| managedStore::managedObjectStore->getSubTree( |
| bmcObjectPath, 0, interfaces, requestContext, |
| [asyncResp, bmcObjectPath]( |
| const boost::system::error_code ec, |
| const dbus::utility::MapperGetSubTreeResponse& subtree) { |
| if (ec) |
| { |
| BMCWEB_LOG_ERROR << "DBus error getting associations for BMC " |
| "inventory object " |
| << ec; |
| return; |
| } |
| |
| // The expected values may be as follows, depending on whether the |
| // static/dynamic stack is used. subtree[0].first: |
| // "/xyz/openbmc_project/inventory/system/bmc/bmc/bmc_diagnostic_data" |
| // subtree[0].second[0]: "xyz.openbmc_project.ObjectMapper" |
| std::optional<std::pair<std::string, std::string>> pathAndService = |
| checkAndGetSoleGetSubTreeResponse(subtree); |
| if (!pathAndService) |
| { |
| BMCWEB_LOG_ERROR << "Couldn't get sole Subtree response"; |
| return; |
| } |
| |
| findBmcAssociationEndpoints(asyncResp, pathAndService->first, |
| pathAndService->second); |
| }); |
| } |
| |
| /** |
| * @brief Find BMC inventories in the system and iterate through them. |
| * |
| * @param[i,o] asyncResp - Async response object |
| * |
| * @return void |
| */ |
| inline void |
| findBmcInventory(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) |
| { |
| std::array<std::string_view, 1> interfaces{ |
| "xyz.openbmc_project.Inventory.Item.Bmc"}; |
| managedStore::ManagedObjectStoreContext requestContext(asyncResp); |
| managedStore::managedObjectStore->getSubTree( |
| "/xyz/openbmc_project/inventory", int32_t(0), interfaces, |
| requestContext, |
| [asyncResp](const boost::system::error_code ec, |
| const dbus::utility::MapperGetSubTreeResponse& subtree) { |
| if (ec) |
| { |
| BMCWEB_LOG_ERROR << "DBus response error on GetSubTree " << ec; |
| return; |
| } |
| |
| // For Entity-Manager based systems ("dynamic inventory stack"): |
| // subtree[0].first may be |
| // "/xyz/openbmc_project/inventory/system/bmc/bmc". |
| // subtree[0].second[0].first may be { |
| // "xyz.openbmc_project.EntityManager", |
| // {"xyz.openbmc_project.AddObject" |
| // "xyz.openbmc_project.Inventory.Item.Bmc"} }. |
| |
| // For Inventory-Manager based systems ("static inventory stack"): |
| // subtree[0].first may be |
| // "/xyz/openbmc_project/inventory/bmc", depending on compile-time |
| // configuration subtree[0].second[0].first may be { |
| // ""xyz.openbmc_project.Inventory.Manager", |
| // {"org.freedesktop.DBus.Introspectable", |
| // "org.freedesktop.DBus.Peer", |
| // "org.freedesktop.DBus.Properties", |
| // "xyz.openbmc_project.Inventory.Item.Bmc"} } |
| std::optional<std::pair<std::string, std::string>> pathAndService = |
| checkAndGetSoleGetSubTreeResponse(subtree); |
| if (!pathAndService) |
| { |
| BMCWEB_LOG_ERROR << "Couldn't get sole Subtree response"; |
| return; |
| } |
| |
| // Found BMC object path. Ask ObjectMapper about the reverse |
| // Association edges. |
| findBmcAssociation(asyncResp, pathAndService->first); |
| }); |
| } |
| |
| void getBootInfo(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) |
| { |
| const std::array<std::string, 2> bootInfoProperties = { |
| {"BootCount", "CrashCount"}}; |
| |
| for (const std::string& propertyName : bootInfoProperties) |
| { |
| managedStore::ManagedObjectStoreContext context(asyncResp); |
| dbus_utils::getProperty<size_t>( |
| "xyz.openbmc_project.Metric", "/xyz/openbmc_project/metric/bmc0", |
| "xyz.openbmc_project.Metric.BMC", propertyName, context, |
| [asyncResp, propertyName](const boost::system::error_code ec, |
| const size_t property) { |
| if (ec) |
| { |
| BMCWEB_LOG_ERROR << "DBus error getting boot count"; |
| return; |
| } |
| |
| asyncResp->res.jsonValue["BootInfo"][propertyName] = property; |
| }); |
| } |
| } |
| |
| inline void calculateBootTimeResp( |
| const std::shared_ptr<bmcweb::AsyncResp>& aResp, |
| const std::vector<std::pair<std::string, dbus::utility::DbusVariantType>>& |
| properties) |
| { |
| static_assert(bootupTimeProperties.size() == |
| bootupTimeMetricName.size() + 1); |
| const size_t kernelIdx = |
| std::find(bootupTimeProperties.begin(), bootupTimeProperties.end(), |
| "KernelTimestampMonotonic") - |
| bootupTimeProperties.begin(); |
| uint64_t lastTime = 0; |
| size_t metricIdx = 0; |
| |
| for (auto& s : bootupTimeMetricName) |
| { |
| aResp->res.jsonValue["BootTimeStatistics"][s] = 0.0; |
| } |
| // each metric should be calculated as next monotime - curr monotime |
| // some timestamps maybe 0 if the hardware doesn't support it. In this |
| // case we simply set that metrics to 0, and accumulate the diff until |
| // an available time is seen |
| // e.g. kernelmono initrdmono usermono finishmono is 0, 4, 0, 10 |
| // The metric values kerneltime initrdtime usertime should be 4, 0, 6 |
| try |
| { |
| for (size_t i = 0; i < bootupTimeProperties.size(); ++i) |
| { |
| const uint64_t* currTime = nullptr; |
| const bool success = sdbusplus::unpackPropertiesNoThrow( |
| dbus_utils::UnpackErrorPrinter(), properties, |
| static_cast<std::string>(bootupTimeProperties[i]), currTime); |
| if (!success || !currTime) |
| { |
| messages::internalError(aResp->res); |
| return; |
| } |
| |
| if (i == 0) |
| { |
| lastTime = *currTime; |
| continue; |
| } |
| uint64_t timeDiff = 0; |
| // KernelTime is 0 and timestamps before kernelTime are negative, |
| // timestamps after kernel are positive. |
| if (*currTime == 0 && i != kernelIdx) |
| { |
| continue; |
| } |
| |
| if (i <= kernelIdx) |
| { |
| if (lastTime != 0) |
| { |
| timeDiff = lastTime - *currTime; |
| } |
| } |
| else |
| { |
| timeDiff = *currTime - lastTime; |
| } |
| |
| lastTime = *currTime; |
| |
| aResp->res.jsonValue["BootTimeStatistics"] |
| [bootupTimeMetricName[metricIdx]] = |
| static_cast<double>(timeDiff) / 1000000.0; |
| metricIdx = i; |
| } |
| } |
| catch (...) |
| { |
| messages::internalError(aResp->res); |
| } |
| } |
| |
| void getBootTimeInfo(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) |
| { |
| managedStore::ManagedObjectStoreContext context(asyncResp); |
| managedStore::managedObjectStore->getAllProperties( |
| "org.freedesktop.systemd1", "/org/freedesktop/systemd1", |
| "org.freedesktop.systemd1.Manager", context, |
| [asyncResp](const boost::system::error_code ec2, |
| const dbus::utility::DBusPropertiesMap& properties) { |
| if (ec2) |
| { |
| BMCWEB_LOG_ERROR << "DBUS error getting boot time"; |
| return; |
| } |
| calculateBootTimeResp(asyncResp, properties); |
| }); |
| } |
| |
| void populateDaemonEntry(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, |
| const std::string objectPath) |
| { |
| managedStore::ManagedObjectStoreContext context(asyncResp); |
| managedStore::managedObjectStore->getAllProperties( |
| "xyz.openbmc_project.Metric", objectPath, |
| "xyz.openbmc_project.Metric.Daemon", context, |
| [asyncResp](const boost::system::error_code ec, |
| const dbus::utility::DBusPropertiesMap& propertyMap) { |
| if (ec) |
| { |
| BMCWEB_LOG_ERROR << "DBus error getting daemon property"; |
| return; |
| } |
| |
| nlohmann::json daemon; |
| |
| for (const auto& propertyPair : propertyMap) |
| { |
| |
| if (propertyPair.first == "CommandLine") |
| { |
| daemon[propertyPair.first] = |
| std::get<std::string>(propertyPair.second); |
| continue; |
| } |
| |
| if (daemonDoublePropertiesSet.contains(propertyPair.first)) |
| { |
| daemon[propertyPair.first] = |
| std::get<double>(propertyPair.second); |
| continue; |
| } |
| |
| if (daemonSizeTPropertiesSet.contains(propertyPair.first)) |
| { |
| daemon[propertyPair.first] = |
| std::get<size_t>(propertyPair.second); |
| continue; |
| } |
| } |
| |
| asyncResp->res.jsonValue["TopProcesses"].emplace_back(daemon); |
| }); |
| } |
| |
| void getDaemonInfo(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) |
| { |
| asyncResp->res.jsonValue["TopProcesses"] = nlohmann::json::array(); |
| |
| managedStore::ManagedObjectStoreContext context(asyncResp); |
| dbus_utils::getProperty<std::vector<std::string>>( |
| "xyz.openbmc_project.ObjectMapper", |
| "/xyz/openbmc_project/metric/bmc0/daemon", |
| "xyz.openbmc_project.Association", "endpoints", context, |
| [asyncResp](const boost::system::error_code ec, |
| const std::vector<std::string>& endpoints) { |
| if (ec) |
| { |
| BMCWEB_LOG_ERROR |
| << "DBus error getting daemon association endpoints"; |
| return; |
| } |
| |
| for (size_t i = 0; i < endpoints.size(); ++i) |
| { |
| populateDaemonEntry(asyncResp, endpoints[i]); |
| } |
| }); |
| } |
| |
| void getLatencyInfo(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) |
| { |
| std::array<std::string, 3> latencyPropertynames{ |
| {"LatencyMin", "LatencyMax", "LatencyAvg"}}; |
| |
| for (const std::string& propertyName : latencyPropertynames) |
| { |
| managedStore::ManagedObjectStoreContext context(asyncResp); |
| dbus_utils::getProperty<double>( |
| "xyz.openbmc_project.Metric", "/xyz/openbmc_project/metric/bmc0", |
| "xyz.openbmc_project.Metric.BMC", propertyName, context, |
| [asyncResp, propertyName](const boost::system::error_code ec, |
| const double property) { |
| if (ec) |
| { |
| BMCWEB_LOG_ERROR |
| << "DBus error getting daemon association endpoints"; |
| return; |
| } |
| |
| asyncResp->res.jsonValue["Oem"]["Google"]["Latency"][propertyName] = |
| property; |
| }); |
| } |
| } |
| |
| inline void |
| afterGetManagerStartTime(const std::shared_ptr<bmcweb::AsyncResp>& aResp, |
| const boost::system::error_code& ec, |
| uint64_t bmcwebResetTime) |
| { |
| if (ec) |
| { |
| // Not all servers will be running in systemd, so ignore the error. |
| return; |
| } |
| using std::chrono::steady_clock; |
| |
| std::chrono::duration<steady_clock::rep, std::micro> usReset{ |
| bmcwebResetTime}; |
| steady_clock::time_point resetTime{usReset}; |
| |
| steady_clock::time_point now = steady_clock::now(); |
| |
| steady_clock::duration runTime = now - resetTime; |
| |
| if (runTime < steady_clock::duration::zero()) |
| { |
| BMCWEB_LOG_CRITICAL << "Uptime was negative????"; |
| messages::internalError(aResp->res); |
| return; |
| } |
| |
| // Floor to the closest millisecond |
| using Milli = std::chrono::duration<steady_clock::rep, std::milli>; |
| Milli milli = std::chrono::floor<Milli>(runTime); |
| |
| using SecondsFloat = std::chrono::duration<double>; |
| SecondsFloat sec = std::chrono::duration_cast<SecondsFloat>(milli); |
| |
| aResp->res.jsonValue["ServiceRootUptimeSeconds"] = sec.count(); |
| } |
| |
| inline void |
| managerGetServiceRootUptime(const std::shared_ptr<bmcweb::AsyncResp>& aResp) |
| { |
| managedStore::ManagedObjectStoreContext context(aResp); |
| dbus_utils::getProperty<uint64_t>( |
| "org.freedesktop.systemd1", |
| "/org/freedesktop/systemd1/unit/bmcweb_2eservice", |
| "org.freedesktop.systemd1.Unit", "ActiveEnterTimestampMonotonic", |
| context, std::bind_front(afterGetManagerStartTime, aResp)); |
| } |
| /** |
| * handleManagerDiagnosticData supports ManagerDiagnosticData. |
| * It retrieves BMC health information from various DBus resources and returns |
| * the information through the response. |
| */ |
| inline void handleManagerDiagnosticDataGet( |
| crow::App& app, const crow::Request& req, |
| const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) |
| { |
| if (!redfish::setUpRedfishRoute(app, req, asyncResp)) |
| { |
| return; |
| } |
| asyncResp->res.jsonValue["@odata.type"] = |
| "#ManagerDiagnosticData.v1_2_0.ManagerDiagnosticData"; |
| asyncResp->res.jsonValue["@odata.id"] = |
| "/redfish/v1/Managers/bmc/ManagerDiagnosticData"; |
| asyncResp->res.jsonValue["Id"] = "ManagerDiagnosticData"; |
| asyncResp->res.jsonValue["Name"] = "Manager Diagnostic Data"; |
| |
| #ifdef BMCWEB_ENABLE_REDFISH_OEM_GRPC_STATS |
| asyncResp->res.jsonValue["Oem"]["Google"]["@odata.type"] = |
| "#GoogleManagerDiagnosticData.v1_0_0.GoogleManagerDiagnosticData"; |
| asyncResp->res.jsonValue["Oem"]["Google"]["gRPCStatistics"]["@odata.id"] = |
| "/redfish/v1/Managers/bmc/ManagerDiagnosticData/Oem/Google/" |
| "GooglegRPCStatistics"; |
| #endif |
| |
| #ifdef BMCWEB_ENABLE_STATEFUL_BMCWEB |
| asyncResp->res.jsonValue["Oem"]["Google"]["GoogleManagedObjectStoreMetrics"] |
| ["@odata.id"] = |
| "/redfish/v1/Managers/bmc/ManagerDiagnosticData/Oem/Google/GoogleManagedObjectStoreMetrics"; |
| #endif |
| |
| managerGetServiceRootUptime(asyncResp); |
| |
| // Start the first step of the 4-step process to populate |
| // the BMC diagnostic data |
| findBmcInventory(asyncResp); |
| getBootInfo(asyncResp); |
| getBootTimeInfo(asyncResp); |
| getDaemonInfo(asyncResp); |
| getLatencyInfo(asyncResp); |
| } |
| |
| #ifdef BMCWEB_ENABLE_REDFISH_OEM_GRPC_STATS |
| |
| inline void handleManagerDiagnosticDataGooglegRPCStatsHead( |
| crow::App& app, const crow::Request& req, |
| const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) |
| { |
| if (!redfish::setUpRedfishRoute(app, req, asyncResp)) |
| { |
| return; |
| } |
| asyncResp->res.addHeader( |
| boost::beast::http::field::link, |
| "</redfish/v1/JsonSchemas/GoogleManagerDiagnosticData/" |
| "GooglegRPCStatistics.json>; rel=describedby"); |
| } |
| |
| inline void handleManagerDiagnosticDataGooglegRPCStatsGet( |
| crow::App& app, const crow::Request& req, |
| const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) |
| { |
| if (!redfish::setUpRedfishRoute(app, req, asyncResp)) |
| { |
| return; |
| } |
| |
| nlohmann::json& j = asyncResp->res.jsonValue; |
| j["@odata.id"] = |
| "/redfish/v1/Managers/bmc/ManagerDiagnosticData/Oem/Google/" |
| "GooglegRPCStatistics"; |
| j["@odata.type"] = "#GooglegRPCStatistics.v1_0_0.GooglegRPCStatistics"; |
| j["Id"] = "GooglegRPCStatistics"; |
| j["Name"] = "Google gRPC Statistics"; |
| |
| grpc_redfish::gRPCStatisticsShared shared_stats; |
| grpc_redfish::gRPCStatistics stats = shared_stats.GetgRPCStatistics(); |
| |
| j["gRPCInitLatencyMs"] = stats.grpc_init_latency_ms.count(); |
| j["AuthenticationLatencyMs"] = stats.authn_latency_total_ms.count(); |
| j["QueueLatencyMs"] = stats.queue_latency_total_ms.count(); |
| j["RequestLatencyMs"] = stats.request_latency_total_ms.count(); |
| j["AuthorizationLatencyMs"] = stats.authz_latency_total_ms.count(); |
| j["ProcessingLatencyMs"] = stats.processing_latency_total_ms.count(); |
| j["ResponseLatencyMs"] = stats.response_latency_total_ms.count(); |
| |
| j["AuthorizedCount"] = stats.total_authorized_count; |
| j["AuthorizedFailCount"] = stats.total_authorized_fail_count; |
| j["AuthenticatedCount"] = stats.total_authenticated_count; |
| j["AuthenticatedFailCount"] = stats.total_authenticated_fail_count; |
| |
| nlohmann::json::array_t httpMethodCounts; |
| for (auto const& [http, count] : stats.request_count) |
| { |
| nlohmann::json::object_t countObj; |
| countObj["Method"] = static_cast<unsigned>(http); |
| countObj["Count"] = static_cast<unsigned>(count); |
| httpMethodCounts.emplace_back(std::move(countObj)); |
| } |
| nlohmann::json::array_t httpResponseCounts; |
| for (auto const& [response, count] : stats.http_response_count) |
| { |
| nlohmann::json::object_t countObj; |
| countObj["StatusCode"] = static_cast<unsigned>(response); |
| countObj["Count"] = static_cast<unsigned>(count); |
| httpResponseCounts.emplace_back(std::move(countObj)); |
| } |
| nlohmann::json::array_t gRPCStatusCounts; |
| for (auto const& [status, count] : stats.grpc_response_count) |
| { |
| nlohmann::json::object_t countObj; |
| countObj["StatusCode"] = static_cast<unsigned>(status); |
| countObj["Count"] = static_cast<unsigned>(count); |
| gRPCStatusCounts.emplace_back(std::move(countObj)); |
| } |
| j["HTTPMethods"] = std::move(httpMethodCounts); |
| j["HTTPResponseCodes"] = std::move(httpResponseCounts); |
| j["gRPCResponseCodes"] = std::move(gRPCStatusCounts); |
| } |
| #endif |
| |
| inline void requestRoutesManagerDiagnosticData(App& app) |
| { |
| BMCWEB_ROUTE(app, "/redfish/v1/Managers/bmc/ManagerDiagnosticData") |
| .privileges(redfish::privileges::getManagerDiagnosticData) |
| .methods(boost::beast::http::verb::get)( |
| std::bind_front(handleManagerDiagnosticDataGet, std::ref(app))); |
| |
| #ifdef BMCWEB_ENABLE_REDFISH_OEM_GRPC_STATS |
| BMCWEB_ROUTE(app, |
| "/redfish/v1/Managers/bmc/ManagerDiagnosticData/Oem/Google/" |
| "GooglegRPCStatistics") |
| .privileges(redfish::privileges::headManagerDiagnosticData) |
| .methods(boost::beast::http::verb::head)(std::bind_front( |
| handleManagerDiagnosticDataGooglegRPCStatsHead, std::ref(app))); |
| |
| BMCWEB_ROUTE(app, |
| "/redfish/v1/Managers/bmc/ManagerDiagnosticData/Oem/Google/" |
| "GooglegRPCStatistics") |
| .privileges(redfish::privileges::getManagerDiagnosticData) |
| .methods(boost::beast::http::verb::get)(std::bind_front( |
| handleManagerDiagnosticDataGooglegRPCStatsGet, std::ref(app))); |
| #endif |
| } |
| |
| } // namespace redfish |