1 // SPDX-License-Identifier: Apache-2.0 2 // SPDX-FileCopyrightText: Copyright OpenBMC Authors 3 #pragma once 4 5 #include "bmcweb_config.h" 6 7 #include "app.hpp" 8 #include "async_resp.hpp" 9 #include "dbus_utility.hpp" 10 #include "error_messages.hpp" 11 #include "http_request.hpp" 12 #include "logging.hpp" 13 #include "query.hpp" 14 #include "registries/privilege_registry.hpp" 15 16 #include <boost/asio/error.hpp> 17 #include <boost/beast/http/verb.hpp> 18 #include <boost/system/error_code.hpp> 19 #include <boost/system/linux_error.hpp> 20 #include <boost/url/format.hpp> 21 #include <nlohmann/json.hpp> 22 23 #include <chrono> 24 #include <cmath> 25 #include <cstdint> 26 #include <functional> 27 #include <memory> 28 #include <ratio> 29 #include <source_location> 30 #include <string> 31 32 namespace redfish 33 { 34 35 static constexpr auto healthMonitorServiceName = 36 "xyz.openbmc_project.HealthMon"; 37 static constexpr auto valueInterface = "xyz.openbmc_project.Metric.Value"; 38 static constexpr auto valueProperty = "Value"; 39 40 inline bool checkErrors( 41 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 42 const boost::system::error_code& ec, 43 const std::source_location src = std::source_location::current()) 44 { 45 if (ec.value() == boost::asio::error::basic_errors::host_unreachable) 46 { 47 BMCWEB_LOG_WARNING("Failed to find server, Dbus error {}", ec); 48 return true; 49 } 50 if (ec.value() == boost::system::linux_error::bad_request_descriptor) 51 { 52 BMCWEB_LOG_WARNING("Invalid Path, Dbus error {}", ec); 53 return true; 54 } 55 if (ec) 56 { 57 BMCWEB_LOG_ERROR("{} failed, error {}", src.function_name(), ec); 58 messages::internalError(asyncResp->res); 59 return true; 60 } 61 return false; 62 } 63 64 inline void 65 setBytesProperty(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 66 const nlohmann::json::json_pointer& jPtr, 67 const boost::system::error_code& ec, double bytes) 68 { 69 if (checkErrors(asyncResp, ec)) 70 { 71 return; 72 } 73 if (!std::isfinite(bytes)) 74 { 75 BMCWEB_LOG_WARNING("Property read for {} was not finite", 76 jPtr.to_string()); 77 asyncResp->res.jsonValue[jPtr] = nullptr; 78 return; 79 } 80 // If the param is in Kib, make it Kib. Redfish uses this as a naming 81 // DBus represents as bytes 82 if (std::string_view(jPtr.back()).ends_with("KiB")) 83 { 84 bytes /= 1024.0; 85 } 86 87 asyncResp->res.jsonValue[jPtr] = static_cast<int64_t>(bytes); 88 } 89 90 inline void managerGetStorageStatistics( 91 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) 92 { 93 constexpr auto freeStorageObjPath = 94 "/xyz/openbmc_project/metric/bmc/storage/rw"; 95 96 dbus::utility::getProperty<double>( 97 healthMonitorServiceName, freeStorageObjPath, valueInterface, 98 valueProperty, 99 std::bind_front(setBytesProperty, asyncResp, 100 nlohmann::json::json_pointer("/FreeStorageSpaceKiB"))); 101 } 102 103 inline void 104 setPercentProperty(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 105 const nlohmann::json::json_pointer& jPtr, 106 const boost::system::error_code& ec, double userCPU) 107 { 108 if (checkErrors(asyncResp, ec)) 109 { 110 return; 111 } 112 if (!std::isfinite(userCPU)) 113 { 114 asyncResp->res.jsonValue[jPtr] = nullptr; 115 return; 116 } 117 118 static constexpr double roundFactor = 10000; // 4 decimal places 119 asyncResp->res.jsonValue[jPtr] = 120 std::round(userCPU * roundFactor) / roundFactor; 121 } 122 123 inline void managerGetProcessorStatistics( 124 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) 125 { 126 constexpr auto kernelCPUObjPath = 127 "/xyz/openbmc_project/metric/bmc/cpu/kernel"; 128 constexpr auto userCPUObjPath = "/xyz/openbmc_project/metric/bmc/cpu/user"; 129 130 using json_pointer = nlohmann::json::json_pointer; 131 dbus::utility::getProperty<double>( 132 healthMonitorServiceName, kernelCPUObjPath, valueInterface, 133 valueProperty, 134 std::bind_front(setPercentProperty, asyncResp, 135 json_pointer("/ProcessorStatistics/KernelPercent"))); 136 137 dbus::utility::getProperty<double>( 138 healthMonitorServiceName, userCPUObjPath, valueInterface, valueProperty, 139 std::bind_front(setPercentProperty, asyncResp, 140 json_pointer("/ProcessorStatistics/UserPercent"))); 141 } 142 143 inline void managerGetMemoryStatistics( 144 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) 145 { 146 using json_pointer = nlohmann::json::json_pointer; 147 constexpr auto availableMemoryObjPath = 148 "/xyz/openbmc_project/metric/bmc/memory/available"; 149 dbus::utility::getProperty<double>( 150 healthMonitorServiceName, availableMemoryObjPath, valueInterface, 151 valueProperty, 152 std::bind_front(setBytesProperty, asyncResp, 153 json_pointer("/MemoryStatistics/AvailableBytes"))); 154 155 constexpr auto bufferedAndCachedMemoryObjPath = 156 "/xyz/openbmc_project/metric/bmc/memory/buffered_and_cached"; 157 dbus::utility::getProperty<double>( 158 healthMonitorServiceName, bufferedAndCachedMemoryObjPath, 159 valueInterface, valueProperty, 160 std::bind_front( 161 setBytesProperty, asyncResp, 162 json_pointer("/MemoryStatistics/BuffersAndCacheBytes"))); 163 164 constexpr auto freeMemoryObjPath = 165 "/xyz/openbmc_project/metric/bmc/memory/free"; 166 dbus::utility::getProperty<double>( 167 healthMonitorServiceName, freeMemoryObjPath, valueInterface, 168 valueProperty, 169 std::bind_front(setBytesProperty, asyncResp, 170 json_pointer("/MemoryStatistics/FreeBytes"))); 171 172 constexpr auto sharedMemoryObjPath = 173 "/xyz/openbmc_project/metric/bmc/memory/shared"; 174 dbus::utility::getProperty<double>( 175 healthMonitorServiceName, sharedMemoryObjPath, valueInterface, 176 valueProperty, 177 std::bind_front(setBytesProperty, asyncResp, 178 json_pointer("/MemoryStatistics/SharedBytes"))); 179 180 constexpr auto totalMemoryObjPath = 181 "/xyz/openbmc_project/metric/bmc/memory/total"; 182 dbus::utility::getProperty<double>( 183 healthMonitorServiceName, totalMemoryObjPath, valueInterface, 184 valueProperty, 185 std::bind_front(setBytesProperty, asyncResp, 186 json_pointer("/MemoryStatistics/TotalBytes"))); 187 } 188 189 inline void afterGetManagerStartTime( 190 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 191 const boost::system::error_code& ec, uint64_t bmcwebResetTime) 192 { 193 if (ec) 194 { 195 // Not all servers will be running in systemd, so ignore the error. 196 return; 197 } 198 using std::chrono::steady_clock; 199 200 std::chrono::duration<steady_clock::rep, std::micro> usReset{ 201 bmcwebResetTime}; 202 steady_clock::time_point resetTime{usReset}; 203 204 steady_clock::time_point now = steady_clock::now(); 205 206 steady_clock::duration runTime = now - resetTime; 207 208 if (runTime < steady_clock::duration::zero()) 209 { 210 BMCWEB_LOG_CRITICAL("Uptime was negative????"); 211 messages::internalError(asyncResp->res); 212 return; 213 } 214 215 // Floor to the closest millisecond 216 using Milli = std::chrono::duration<steady_clock::rep, std::milli>; 217 Milli milli = std::chrono::floor<Milli>(runTime); 218 219 using SecondsFloat = std::chrono::duration<double>; 220 SecondsFloat sec = std::chrono::duration_cast<SecondsFloat>(milli); 221 222 asyncResp->res.jsonValue["ServiceRootUptimeSeconds"] = sec.count(); 223 } 224 225 inline void managerGetServiceRootUptime( 226 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) 227 { 228 dbus::utility::getProperty<uint64_t>( 229 "org.freedesktop.systemd1", 230 "/org/freedesktop/systemd1/unit/bmcweb_2eservice", 231 "org.freedesktop.systemd1.Unit", "ActiveEnterTimestampMonotonic", 232 std::bind_front(afterGetManagerStartTime, asyncResp)); 233 } 234 /** 235 * handleManagerDiagnosticData supports ManagerDiagnosticData. 236 * It retrieves BMC health information from various DBus resources and returns 237 * the information through the response. 238 */ 239 inline void handleManagerDiagnosticDataGet( 240 crow::App& app, const crow::Request& req, 241 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 242 const std::string& managerId) 243 { 244 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 245 { 246 return; 247 } 248 249 if (managerId != BMCWEB_REDFISH_MANAGER_URI_NAME) 250 { 251 messages::resourceNotFound(asyncResp->res, "Manager", managerId); 252 return; 253 } 254 255 asyncResp->res.jsonValue["@odata.type"] = 256 "#ManagerDiagnosticData.v1_2_0.ManagerDiagnosticData"; 257 asyncResp->res.jsonValue["@odata.id"] = 258 boost::urls::format("/redfish/v1/Managers/{}/ManagerDiagnosticData", 259 BMCWEB_REDFISH_MANAGER_URI_NAME); 260 asyncResp->res.jsonValue["Id"] = "ManagerDiagnosticData"; 261 asyncResp->res.jsonValue["Name"] = "Manager Diagnostic Data"; 262 263 managerGetServiceRootUptime(asyncResp); 264 managerGetProcessorStatistics(asyncResp); 265 managerGetMemoryStatistics(asyncResp); 266 managerGetStorageStatistics(asyncResp); 267 } 268 269 inline void requestRoutesManagerDiagnosticData(App& app) 270 { 271 BMCWEB_ROUTE(app, "/redfish/v1/Managers/<str>/ManagerDiagnosticData") 272 .privileges(redfish::privileges::getManagerDiagnosticData) 273 .methods(boost::beast::http::verb::get)( 274 std::bind_front(handleManagerDiagnosticDataGet, std::ref(app))); 275 } 276 277 } // namespace redfish 278