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