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