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
checkErrors(const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const boost::system::error_code & ec,const std::source_location src=std::source_location::current ())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
setBytesProperty(const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const nlohmann::json::json_pointer & jPtr,const boost::system::error_code & ec,double bytes)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
managerGetStorageStatistics(const std::shared_ptr<bmcweb::AsyncResp> & asyncResp)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
setPercentProperty(const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const nlohmann::json::json_pointer & jPtr,const boost::system::error_code & ec,double userCPU)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
managerGetProcessorStatistics(const std::shared_ptr<bmcweb::AsyncResp> & asyncResp)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
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 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
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 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 */
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