xref: /openbmc/bmcweb/redfish-core/lib/manager_diagnostic_data.hpp (revision 40e9b92ec19acffb46f83a6e55b18974da5d708e)
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