1 #pragma once 2 3 #include "app.hpp" 4 #include "async_resp.hpp" 5 #include "dbus_utility.hpp" 6 #include "http_request.hpp" 7 #include "privileges.hpp" 8 #include "query.hpp" 9 #include "registries/privilege_registry.hpp" 10 #include "routing.hpp" 11 12 #include <boost/system/error_code.hpp> 13 #include <boost/system/linux_error.hpp> 14 #include <boost/url/format.hpp> 15 #include <nlohmann/json.hpp> 16 #include <sdbusplus/asio/property.hpp> 17 18 #include <functional> 19 #include <limits> 20 #include <memory> 21 #include <string> 22 23 namespace redfish 24 { 25 26 static constexpr auto healthMonitorServiceName = 27 "xyz.openbmc_project.HealthMon"; 28 static constexpr auto valueInterface = "xyz.openbmc_project.Metric.Value"; 29 static constexpr auto valueProperty = "Value"; 30 31 inline bool checkErrors( 32 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 33 const boost::system::error_code& ec, 34 const std::source_location src = std::source_location::current()) 35 { 36 if (ec.value() == boost::asio::error::basic_errors::host_unreachable) 37 { 38 BMCWEB_LOG_WARNING("Failed to find server, Dbus error {}", ec); 39 return true; 40 } 41 if (ec.value() == boost::system::linux_error::bad_request_descriptor) 42 { 43 BMCWEB_LOG_WARNING("Invalid Path, Dbus error {}", ec); 44 return true; 45 } 46 if (ec) 47 { 48 BMCWEB_LOG_ERROR("{} failed, error {}", src.function_name(), ec); 49 messages::internalError(asyncResp->res); 50 return true; 51 } 52 return false; 53 } 54 55 inline void 56 setBytesProperty(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 57 const nlohmann::json::json_pointer& jPtr, 58 const boost::system::error_code& ec, double bytes) 59 { 60 if (checkErrors(asyncResp, ec)) 61 { 62 return; 63 } 64 if (!std::isfinite(bytes)) 65 { 66 BMCWEB_LOG_WARNING("Property read for {} was not finite", 67 jPtr.to_string()); 68 asyncResp->res.jsonValue[jPtr] = nullptr; 69 return; 70 } 71 // If the param is in Kib, make it Kib. Redfish uses this as a naming 72 // DBus represents as bytes 73 if (std::string_view(jPtr.back()).ends_with("KiB")) 74 { 75 bytes /= 1024.0; 76 } 77 78 asyncResp->res.jsonValue[jPtr] = static_cast<int64_t>(bytes); 79 } 80 81 inline void managerGetStorageStatistics( 82 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) 83 { 84 constexpr auto freeStorageObjPath = 85 "/xyz/openbmc_project/metric/bmc/storage/rw"; 86 87 dbus::utility::getProperty<double>( 88 healthMonitorServiceName, freeStorageObjPath, valueInterface, 89 valueProperty, 90 std::bind_front(setBytesProperty, asyncResp, 91 nlohmann::json::json_pointer("/FreeStorageSpaceKiB"))); 92 } 93 94 inline void 95 setPercentProperty(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 96 const nlohmann::json::json_pointer& jPtr, 97 const boost::system::error_code& ec, double userCPU) 98 { 99 if (checkErrors(asyncResp, ec)) 100 { 101 return; 102 } 103 if (!std::isfinite(userCPU)) 104 { 105 asyncResp->res.jsonValue[jPtr] = nullptr; 106 return; 107 } 108 109 static constexpr double roundFactor = 10000; // 4 decimal places 110 asyncResp->res.jsonValue[jPtr] = 111 std::round(userCPU * roundFactor) / roundFactor; 112 } 113 114 inline void managerGetProcessorStatistics( 115 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) 116 { 117 constexpr auto kernelCPUObjPath = 118 "/xyz/openbmc_project/metric/bmc/cpu/kernel"; 119 constexpr auto userCPUObjPath = "/xyz/openbmc_project/metric/bmc/cpu/user"; 120 121 using json_pointer = nlohmann::json::json_pointer; 122 dbus::utility::getProperty<double>( 123 healthMonitorServiceName, kernelCPUObjPath, valueInterface, 124 valueProperty, 125 std::bind_front(setPercentProperty, asyncResp, 126 json_pointer("/ProcessorStatistics/KernelPercent"))); 127 128 dbus::utility::getProperty<double>( 129 healthMonitorServiceName, userCPUObjPath, 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 dbus::utility::getProperty<double>( 141 healthMonitorServiceName, availableMemoryObjPath, valueInterface, 142 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 dbus::utility::getProperty<double>( 149 healthMonitorServiceName, bufferedAndCachedMemoryObjPath, 150 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 dbus::utility::getProperty<double>( 158 healthMonitorServiceName, freeMemoryObjPath, valueInterface, 159 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 dbus::utility::getProperty<double>( 166 healthMonitorServiceName, sharedMemoryObjPath, valueInterface, 167 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 dbus::utility::getProperty<double>( 174 healthMonitorServiceName, totalMemoryObjPath, valueInterface, 175 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 dbus::utility::getProperty<uint64_t>( 220 "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