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