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
checkErrors(const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const boost::system::error_code & ec,const std::source_location src=std::source_location::current ())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
setBytesProperty(const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const nlohmann::json::json_pointer & jPtr,const boost::system::error_code & ec,double bytes)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
managerGetStorageStatistics(const std::shared_ptr<bmcweb::AsyncResp> & asyncResp)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
setPercentProperty(const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const nlohmann::json::json_pointer & jPtr,const boost::system::error_code & ec,double userCPU)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] =
110 std::round(userCPU * roundFactor) / roundFactor;
111 }
112
managerGetProcessorStatistics(const std::shared_ptr<bmcweb::AsyncResp> & asyncResp)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
managerGetMemoryStatistics(const std::shared_ptr<bmcweb::AsyncResp> & asyncResp)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
afterGetManagerStartTime(const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const boost::system::error_code & ec,uint64_t bmcwebResetTime)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
managerGetServiceRootUptime(const std::shared_ptr<bmcweb::AsyncResp> & asyncResp)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 */
handleManagerDiagnosticDataGet(crow::App & app,const crow::Request & req,const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & managerId)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
requestRoutesManagerDiagnosticData(App & app)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