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