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