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 <boost/url/format.hpp>
14 #include <nlohmann/json.hpp>
15 #include <sdbusplus/asio/property.hpp>
16 
17 #include <functional>
18 #include <limits>
19 #include <memory>
20 #include <string>
21 
22 namespace redfish
23 {
24 
25 static constexpr auto healthMonitorServiceName =
26     "xyz.openbmc_project.HealthMon";
27 static constexpr auto valueInterface = "xyz.openbmc_project.Metric.Value";
28 static constexpr auto valueProperty = "Value";
29 
checkErrors(const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const boost::system::error_code & ec,const std::source_location src=std::source_location::current ())30 inline bool checkErrors(
31     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
32     const boost::system::error_code& ec,
33     const std::source_location src = std::source_location::current())
34 {
35     if (ec.value() == boost::asio::error::basic_errors::host_unreachable)
36     {
37         BMCWEB_LOG_WARNING("Failed to find server, Dbus error {}", ec);
38         return true;
39     }
40     if (ec.value() == boost::system::linux_error::bad_request_descriptor)
41     {
42         BMCWEB_LOG_WARNING("Invalid Path, Dbus error {}", ec);
43         return true;
44     }
45     if (ec)
46     {
47         BMCWEB_LOG_ERROR("{} failed, error {}", src.function_name(), ec);
48         messages::internalError(asyncResp->res);
49         return true;
50     }
51     return false;
52 }
53 
54 inline void
setBytesProperty(const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const nlohmann::json::json_pointer & jPtr,const boost::system::error_code & ec,double bytes)55     setBytesProperty(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
56                      const nlohmann::json::json_pointer& jPtr,
57                      const boost::system::error_code& ec, double bytes)
58 {
59     if (checkErrors(asyncResp, ec))
60     {
61         return;
62     }
63     if (!std::isfinite(bytes))
64     {
65         BMCWEB_LOG_WARNING("Property read for {} was not finite",
66                            jPtr.to_string());
67         asyncResp->res.jsonValue[jPtr] = nullptr;
68         return;
69     }
70     // If the param is in Kib, make it Kib.  Redfish uses this as a naming
71     // DBus represents as bytes
72     if (std::string_view(jPtr.back()).ends_with("KiB"))
73     {
74         bytes /= 1024.0;
75     }
76 
77     asyncResp->res.jsonValue[jPtr] = static_cast<int64_t>(bytes);
78 }
79 
managerGetStorageStatistics(const std::shared_ptr<bmcweb::AsyncResp> & asyncResp)80 inline void managerGetStorageStatistics(
81     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
82 {
83     constexpr auto freeStorageObjPath =
84         "/xyz/openbmc_project/metric/bmc/storage/rw";
85 
86     sdbusplus::asio::getProperty<double>(
87         *crow::connections::systemBus, healthMonitorServiceName,
88         freeStorageObjPath, valueInterface, valueProperty,
89         std::bind_front(setBytesProperty, asyncResp,
90                         nlohmann::json::json_pointer("/FreeStorageSpaceKiB")));
91 }
92 
93 inline void
setPercentProperty(const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const nlohmann::json::json_pointer & jPtr,const boost::system::error_code & ec,double userCPU)94     setPercentProperty(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
95                        const nlohmann::json::json_pointer& jPtr,
96                        const boost::system::error_code& ec, double userCPU)
97 {
98     if (checkErrors(asyncResp, ec))
99     {
100         return;
101     }
102     if (!std::isfinite(userCPU))
103     {
104         asyncResp->res.jsonValue[jPtr] = nullptr;
105         return;
106     }
107 
108     static constexpr double roundFactor = 10000; // 4 decimal places
109     asyncResp->res.jsonValue[jPtr] =
110         std::round(userCPU * roundFactor) / roundFactor;
111 }
112 
managerGetProcessorStatistics(const std::shared_ptr<bmcweb::AsyncResp> & asyncResp)113 inline void managerGetProcessorStatistics(
114     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
115 {
116     constexpr auto kernelCPUObjPath =
117         "/xyz/openbmc_project/metric/bmc/cpu/kernel";
118     constexpr auto userCPUObjPath = "/xyz/openbmc_project/metric/bmc/cpu/user";
119 
120     using json_pointer = nlohmann::json::json_pointer;
121     sdbusplus::asio::getProperty<double>(
122         *crow::connections::systemBus, healthMonitorServiceName,
123         kernelCPUObjPath, valueInterface, valueProperty,
124         std::bind_front(setPercentProperty, asyncResp,
125                         json_pointer("/ProcessorStatistics/KernelPercent")));
126 
127     sdbusplus::asio::getProperty<double>(
128         *crow::connections::systemBus, healthMonitorServiceName, userCPUObjPath,
129         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     sdbusplus::asio::getProperty<double>(
141         *crow::connections::systemBus, healthMonitorServiceName,
142         availableMemoryObjPath, valueInterface, 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     sdbusplus::asio::getProperty<double>(
149         *crow::connections::systemBus, healthMonitorServiceName,
150         bufferedAndCachedMemoryObjPath, 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     sdbusplus::asio::getProperty<double>(
158         *crow::connections::systemBus, healthMonitorServiceName,
159         freeMemoryObjPath, valueInterface, 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     sdbusplus::asio::getProperty<double>(
166         *crow::connections::systemBus, healthMonitorServiceName,
167         sharedMemoryObjPath, valueInterface, 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     sdbusplus::asio::getProperty<double>(
174         *crow::connections::systemBus, healthMonitorServiceName,
175         totalMemoryObjPath, valueInterface, 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     sdbusplus::asio::getProperty<uint64_t>(
220         *crow::connections::systemBus, "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