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