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