blob: 0566e93923c9aa919452b6f7d12d9562116a14a7 [file] [log] [blame]
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +01001/*
2// Copyright (c) 2018 Intel Corporation
3//
4// Licensed under the Apache License, Version 2.0 (the "License");
5// you may not use this file except in compliance with the License.
6// You may obtain a copy of the License at
7//
8// http://www.apache.org/licenses/LICENSE-2.0
9//
10// Unless required by applicable law or agreed to in writing, software
11// distributed under the License is distributed on an "AS IS" BASIS,
12// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13// See the License for the specific language governing permissions and
14// limitations under the License.
15*/
16#pragma once
17
John Edward Broadbent7e860f12021-04-08 15:57:16 -070018#include <app.hpp>
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +010019#include <boost/algorithm/string/predicate.hpp>
20#include <boost/algorithm/string/split.hpp>
21#include <boost/container/flat_map.hpp>
22#include <boost/range/algorithm/replace_copy_if.hpp>
Ed Tanous1abe55e2018-09-05 08:30:59 -070023#include <dbus_singleton.hpp>
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +053024#include <utils/json_utils.hpp>
Gunnar Mills1214b7e2020-06-04 10:11:30 -050025
26#include <cmath>
Ed Tanousb5a76932020-09-29 16:16:58 -070027#include <utility>
Ed Tanousabf2add2019-01-22 16:40:12 -080028#include <variant>
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +010029
Ed Tanous1abe55e2018-09-05 08:30:59 -070030namespace redfish
31{
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +010032
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +010033using GetSubTreeType = std::vector<
34 std::pair<std::string,
35 std::vector<std::pair<std::string, std::vector<std::string>>>>>;
36
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -050037using SensorVariant =
38 std::variant<int64_t, double, uint32_t, bool, std::string>;
Ed Tanousaa2e59c2018-04-12 12:17:20 -070039
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +010040using ManagedObjectsVectorType = std::vector<std::pair<
Ed Tanousaa2e59c2018-04-12 12:17:20 -070041 sdbusplus::message::object_path,
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +010042 boost::container::flat_map<
Ed Tanousaa2e59c2018-04-12 12:17:20 -070043 std::string, boost::container::flat_map<std::string, SensorVariant>>>>;
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +010044
Adrian Ambrożewicza0ec28b2020-04-10 14:47:28 +020045namespace sensors
46{
47namespace node
48{
49static constexpr std::string_view power = "Power";
50static constexpr std::string_view sensors = "Sensors";
51static constexpr std::string_view thermal = "Thermal";
52} // namespace node
53
54namespace dbus
55{
Wludzik, Jozefc2bf7f92021-03-08 14:35:54 +000056
Adrian Ambrożewicza0ec28b2020-04-10 14:47:28 +020057static const boost::container::flat_map<std::string_view,
58 std::vector<const char*>>
Wludzik, Jozefc2bf7f92021-03-08 14:35:54 +000059 paths = {{node::power,
Adrian Ambrożewicza0ec28b2020-04-10 14:47:28 +020060 {"/xyz/openbmc_project/sensors/voltage",
61 "/xyz/openbmc_project/sensors/power"}},
62 {node::sensors,
63 {"/xyz/openbmc_project/sensors/power",
64 "/xyz/openbmc_project/sensors/current",
65 "/xyz/openbmc_project/sensors/utilization"}},
66 {node::thermal,
67 {"/xyz/openbmc_project/sensors/fan_tach",
68 "/xyz/openbmc_project/sensors/temperature",
69 "/xyz/openbmc_project/sensors/fan_pwm"}}};
Wludzik, Jozefc2bf7f92021-03-08 14:35:54 +000070} // namespace dbus
71
72inline const char* toReadingType(const std::string& sensorType)
73{
74 if (sensorType == "voltage")
75 {
76 return "Voltage";
77 }
78 if (sensorType == "power")
79 {
80 return "Power";
81 }
82 if (sensorType == "current")
83 {
84 return "Current";
85 }
86 if (sensorType == "fan_tach")
87 {
88 return "Rotational";
89 }
90 if (sensorType == "temperature")
91 {
92 return "Temperature";
93 }
94 if (sensorType == "fan_pwm" || sensorType == "utilization")
95 {
96 return "Percent";
97 }
98 if (sensorType == "altitude")
99 {
100 return "Altitude";
101 }
102 if (sensorType == "airflow")
103 {
104 return "AirFlow";
105 }
106 if (sensorType == "energy")
107 {
108 return "EnergyJoules";
109 }
110 return "";
111}
112
113inline const char* toReadingUnits(const std::string& sensorType)
114{
115 if (sensorType == "voltage")
116 {
117 return "V";
118 }
119 if (sensorType == "power")
120 {
121 return "W";
122 }
123 if (sensorType == "current")
124 {
125 return "A";
126 }
127 if (sensorType == "fan_tach")
128 {
129 return "RPM";
130 }
131 if (sensorType == "temperature")
132 {
133 return "Cel";
134 }
135 if (sensorType == "fan_pwm" || sensorType == "utilization")
136 {
137 return "%";
138 }
139 if (sensorType == "altitude")
140 {
141 return "m";
142 }
143 if (sensorType == "airflow")
144 {
145 return "cft_i/min";
146 }
147 if (sensorType == "energy")
148 {
149 return "J";
150 }
151 return "";
Adrian Ambrożewicza0ec28b2020-04-10 14:47:28 +0200152}
153} // namespace sensors
154
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +0100155/**
Kowalski, Kamil588c3f02018-04-03 14:55:27 +0200156 * SensorsAsyncResp
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +0100157 * Gathers data needed for response processing after async calls are done
158 */
Ed Tanous1abe55e2018-09-05 08:30:59 -0700159class SensorsAsyncResp
160{
161 public:
Adrian Ambrożewicza0ec28b2020-04-10 14:47:28 +0200162 using DataCompleteCb = std::function<void(
163 const boost::beast::http::status status,
164 const boost::container::flat_map<std::string, std::string>& uriToDbus)>;
165
166 struct SensorData
167 {
168 const std::string name;
169 std::string uri;
170 const std::string valueKey;
171 const std::string dbusPath;
172 };
173
zhanghch058d1b46d2021-04-01 11:18:24 +0800174 SensorsAsyncResp(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
175 const std::string& chassisIdIn,
Ed Tanousb5a76932020-09-29 16:16:58 -0700176 const std::vector<const char*>& typesIn,
Adrian Ambrożewicza0ec28b2020-04-10 14:47:28 +0200177 const std::string_view& subNode) :
zhanghch058d1b46d2021-04-01 11:18:24 +0800178 asyncResp(asyncResp),
Ed Tanous271584a2019-07-09 16:24:22 -0700179 chassisId(chassisIdIn), types(typesIn), chassisSubNode(subNode)
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500180 {}
Kowalski, Kamil588c3f02018-04-03 14:55:27 +0200181
Adrian Ambrożewicza0ec28b2020-04-10 14:47:28 +0200182 // Store extra data about sensor mapping and return it in callback
zhanghch058d1b46d2021-04-01 11:18:24 +0800183 SensorsAsyncResp(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
184 const std::string& chassisIdIn,
Ed Tanousb5a76932020-09-29 16:16:58 -0700185 const std::vector<const char*>& typesIn,
Adrian Ambrożewicza0ec28b2020-04-10 14:47:28 +0200186 const std::string_view& subNode,
187 DataCompleteCb&& creationComplete) :
zhanghch058d1b46d2021-04-01 11:18:24 +0800188 asyncResp(asyncResp),
Adrian Ambrożewicza0ec28b2020-04-10 14:47:28 +0200189 chassisId(chassisIdIn), types(typesIn),
190 chassisSubNode(subNode), metadata{std::vector<SensorData>()},
191 dataComplete{std::move(creationComplete)}
192 {}
193
Ed Tanous1abe55e2018-09-05 08:30:59 -0700194 ~SensorsAsyncResp()
195 {
zhanghch058d1b46d2021-04-01 11:18:24 +0800196 if (asyncResp->res.result() ==
197 boost::beast::http::status::internal_server_error)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700198 {
199 // Reset the json object to clear out any data that made it in
200 // before the error happened todo(ed) handle error condition with
201 // proper code
zhanghch058d1b46d2021-04-01 11:18:24 +0800202 asyncResp->res.jsonValue = nlohmann::json::object();
Ed Tanous1abe55e2018-09-05 08:30:59 -0700203 }
Adrian Ambrożewicza0ec28b2020-04-10 14:47:28 +0200204
205 if (dataComplete && metadata)
206 {
207 boost::container::flat_map<std::string, std::string> map;
zhanghch058d1b46d2021-04-01 11:18:24 +0800208 if (asyncResp->res.result() == boost::beast::http::status::ok)
Adrian Ambrożewicza0ec28b2020-04-10 14:47:28 +0200209 {
210 for (auto& sensor : *metadata)
211 {
212 map.insert(std::make_pair(sensor.uri + sensor.valueKey,
213 sensor.dbusPath));
214 }
215 }
zhanghch058d1b46d2021-04-01 11:18:24 +0800216 dataComplete(asyncResp->res.result(), map);
Adrian Ambrożewicza0ec28b2020-04-10 14:47:28 +0200217 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700218 }
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +0100219
Adrian Ambrożewicza0ec28b2020-04-10 14:47:28 +0200220 void addMetadata(const nlohmann::json& sensorObject,
221 const std::string& valueKey, const std::string& dbusPath)
222 {
223 if (metadata)
224 {
225 metadata->emplace_back(SensorData{sensorObject["Name"],
226 sensorObject["@odata.id"],
227 valueKey, dbusPath});
228 }
229 }
230
231 void updateUri(const std::string& name, const std::string& uri)
232 {
233 if (metadata)
234 {
235 for (auto& sensor : *metadata)
236 {
237 if (sensor.name == name)
238 {
239 sensor.uri = uri;
240 }
241 }
242 }
243 }
244
zhanghch058d1b46d2021-04-01 11:18:24 +0800245 const std::shared_ptr<bmcweb::AsyncResp> asyncResp;
Adrian Ambrożewicza0ec28b2020-04-10 14:47:28 +0200246 const std::string chassisId;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700247 const std::vector<const char*> types;
Adrian Ambrożewicza0ec28b2020-04-10 14:47:28 +0200248 const std::string chassisSubNode;
249
250 private:
251 std::optional<std::vector<SensorData>> metadata;
252 DataCompleteCb dataComplete;
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +0100253};
254
255/**
Anthony Wilsond5005492019-07-31 16:34:17 -0500256 * Possible states for physical inventory leds
257 */
258enum class LedState
259{
260 OFF,
261 ON,
262 BLINK,
263 UNKNOWN
264};
265
266/**
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -0500267 * D-Bus inventory item associated with one or more sensors.
268 */
269class InventoryItem
270{
271 public:
272 InventoryItem(const std::string& objPath) :
273 objectPath(objPath), name(), isPresent(true), isFunctional(true),
Gunnar Mills42cbe532019-08-15 15:26:54 -0500274 isPowerSupply(false), powerSupplyEfficiencyPercent(-1), manufacturer(),
275 model(), partNumber(), serialNumber(), sensors(), ledObjectPath(""),
Anthony Wilsond5005492019-07-31 16:34:17 -0500276 ledState(LedState::UNKNOWN)
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -0500277 {
278 // Set inventory item name to last node of object path
George Liu28aa8de2021-02-01 15:13:30 +0800279 sdbusplus::message::object_path path(objectPath);
280 name = path.filename();
281 if (name.empty())
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -0500282 {
George Liu28aa8de2021-02-01 15:13:30 +0800283 BMCWEB_LOG_ERROR << "Failed to find '/' in " << objectPath;
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -0500284 }
285 }
286
287 std::string objectPath;
288 std::string name;
289 bool isPresent;
290 bool isFunctional;
291 bool isPowerSupply;
Gunnar Mills42cbe532019-08-15 15:26:54 -0500292 int powerSupplyEfficiencyPercent;
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -0500293 std::string manufacturer;
294 std::string model;
295 std::string partNumber;
296 std::string serialNumber;
297 std::set<std::string> sensors;
Anthony Wilsond5005492019-07-31 16:34:17 -0500298 std::string ledObjectPath;
299 LedState ledState;
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -0500300};
301
302/**
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +0530303 * @brief Get objects with connection necessary for sensors
Kowalski, Kamil588c3f02018-04-03 14:55:27 +0200304 * @param SensorsAsyncResp Pointer to object holding response data
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +0100305 * @param sensorNames Sensors retrieved from chassis
306 * @param callback Callback for processing gathered connections
307 */
308template <typename Callback>
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +0530309void getObjectsWithConnection(
Ed Tanous81ce6092020-12-17 16:54:55 +0000310 const std::shared_ptr<SensorsAsyncResp>& sensorsAsyncResp,
Ed Tanousb5a76932020-09-29 16:16:58 -0700311 const std::shared_ptr<boost::container::flat_set<std::string>>& sensorNames,
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +0530312 Callback&& callback)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700313{
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +0530314 BMCWEB_LOG_DEBUG << "getObjectsWithConnection enter";
Ed Tanous1abe55e2018-09-05 08:30:59 -0700315 const std::string path = "/xyz/openbmc_project/sensors";
316 const std::array<std::string, 1> interfaces = {
317 "xyz.openbmc_project.Sensor.Value"};
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +0100318
Ed Tanous1abe55e2018-09-05 08:30:59 -0700319 // Response handler for parsing objects subtree
Ed Tanous81ce6092020-12-17 16:54:55 +0000320 auto respHandler = [callback{std::move(callback)}, sensorsAsyncResp,
Ed Tanous1abe55e2018-09-05 08:30:59 -0700321 sensorNames](const boost::system::error_code ec,
322 const GetSubTreeType& subtree) {
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +0530323 BMCWEB_LOG_DEBUG << "getObjectsWithConnection resp_handler enter";
Ed Tanous1abe55e2018-09-05 08:30:59 -0700324 if (ec)
325 {
zhanghch058d1b46d2021-04-01 11:18:24 +0800326 messages::internalError(sensorsAsyncResp->asyncResp->res);
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +0530327 BMCWEB_LOG_ERROR
328 << "getObjectsWithConnection resp_handler: Dbus error " << ec;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700329 return;
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +0100330 }
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +0100331
Ed Tanous1abe55e2018-09-05 08:30:59 -0700332 BMCWEB_LOG_DEBUG << "Found " << subtree.size() << " subtrees";
333
334 // Make unique list of connections only for requested sensor types and
335 // found in the chassis
336 boost::container::flat_set<std::string> connections;
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +0530337 std::set<std::pair<std::string, std::string>> objectsWithConnection;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700338 // Intrinsic to avoid malloc. Most systems will have < 8 sensor
339 // producers
340 connections.reserve(8);
341
Johnathan Mantey49c53ac2019-05-02 09:22:38 -0700342 BMCWEB_LOG_DEBUG << "sensorNames list count: " << sensorNames->size();
343 for (const std::string& tsensor : *sensorNames)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700344 {
345 BMCWEB_LOG_DEBUG << "Sensor to find: " << tsensor;
346 }
347
348 for (const std::pair<
349 std::string,
350 std::vector<std::pair<std::string, std::vector<std::string>>>>&
351 object : subtree)
352 {
Johnathan Mantey49c53ac2019-05-02 09:22:38 -0700353 if (sensorNames->find(object.first) != sensorNames->end())
Ed Tanous1abe55e2018-09-05 08:30:59 -0700354 {
Johnathan Mantey49c53ac2019-05-02 09:22:38 -0700355 for (const std::pair<std::string, std::vector<std::string>>&
356 objData : object.second)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700357 {
Johnathan Mantey49c53ac2019-05-02 09:22:38 -0700358 BMCWEB_LOG_DEBUG << "Adding connection: " << objData.first;
359 connections.insert(objData.first);
360 objectsWithConnection.insert(
361 std::make_pair(object.first, objData.first));
Ed Tanous1abe55e2018-09-05 08:30:59 -0700362 }
363 }
364 }
365 BMCWEB_LOG_DEBUG << "Found " << connections.size() << " connections";
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +0530366 callback(std::move(connections), std::move(objectsWithConnection));
367 BMCWEB_LOG_DEBUG << "getObjectsWithConnection resp_handler exit";
Ed Tanous1abe55e2018-09-05 08:30:59 -0700368 };
Ed Tanous1abe55e2018-09-05 08:30:59 -0700369 // Make call to ObjectMapper to find all sensors objects
370 crow::connections::systemBus->async_method_call(
371 std::move(respHandler), "xyz.openbmc_project.ObjectMapper",
372 "/xyz/openbmc_project/object_mapper",
373 "xyz.openbmc_project.ObjectMapper", "GetSubTree", path, 2, interfaces);
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +0530374 BMCWEB_LOG_DEBUG << "getObjectsWithConnection exit";
375}
376
377/**
378 * @brief Create connections necessary for sensors
379 * @param SensorsAsyncResp Pointer to object holding response data
380 * @param sensorNames Sensors retrieved from chassis
381 * @param callback Callback for processing gathered connections
382 */
383template <typename Callback>
Johnathan Mantey49c53ac2019-05-02 09:22:38 -0700384void getConnections(
Ed Tanous81ce6092020-12-17 16:54:55 +0000385 std::shared_ptr<SensorsAsyncResp> sensorsAsyncResp,
Johnathan Mantey49c53ac2019-05-02 09:22:38 -0700386 const std::shared_ptr<boost::container::flat_set<std::string>> sensorNames,
387 Callback&& callback)
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +0530388{
389 auto objectsWithConnectionCb =
390 [callback](const boost::container::flat_set<std::string>& connections,
391 const std::set<std::pair<std::string, std::string>>&
Ed Tanous3174e4d2020-10-07 11:41:22 -0700392 /*objectsWithConnection*/) { callback(connections); };
Ed Tanous81ce6092020-12-17 16:54:55 +0000393 getObjectsWithConnection(sensorsAsyncResp, sensorNames,
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +0530394 std::move(objectsWithConnectionCb));
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +0100395}
396
397/**
Johnathan Mantey49c53ac2019-05-02 09:22:38 -0700398 * @brief Shrinks the list of sensors for processing
399 * @param SensorsAysncResp The class holding the Redfish response
400 * @param allSensors A list of all the sensors associated to the
401 * chassis element (i.e. baseboard, front panel, etc...)
402 * @param activeSensors A list that is a reduction of the incoming
403 * allSensors list. Eliminate Thermal sensors when a Power request is
404 * made, and eliminate Power sensors when a Thermal request is made.
405 */
Ed Tanous23a21a12020-07-25 04:45:05 +0000406inline void reduceSensorList(
Ed Tanous81ce6092020-12-17 16:54:55 +0000407 const std::shared_ptr<SensorsAsyncResp>& sensorsAsyncResp,
Johnathan Mantey49c53ac2019-05-02 09:22:38 -0700408 const std::vector<std::string>* allSensors,
Ed Tanousb5a76932020-09-29 16:16:58 -0700409 const std::shared_ptr<boost::container::flat_set<std::string>>&
410 activeSensors)
Johnathan Mantey49c53ac2019-05-02 09:22:38 -0700411{
Ed Tanous81ce6092020-12-17 16:54:55 +0000412 if (sensorsAsyncResp == nullptr)
Johnathan Mantey49c53ac2019-05-02 09:22:38 -0700413 {
414 return;
415 }
416 if ((allSensors == nullptr) || (activeSensors == nullptr))
417 {
418 messages::resourceNotFound(
zhanghch058d1b46d2021-04-01 11:18:24 +0800419 sensorsAsyncResp->asyncResp->res, sensorsAsyncResp->chassisSubNode,
Ed Tanous81ce6092020-12-17 16:54:55 +0000420 sensorsAsyncResp->chassisSubNode == sensors::node::thermal
Adrian Ambrożewicza0ec28b2020-04-10 14:47:28 +0200421 ? "Temperatures"
422 : "Voltages");
Johnathan Mantey49c53ac2019-05-02 09:22:38 -0700423
424 return;
425 }
426 if (allSensors->empty())
427 {
428 // Nothing to do, the activeSensors object is also empty
429 return;
430 }
431
Ed Tanous81ce6092020-12-17 16:54:55 +0000432 for (const char* type : sensorsAsyncResp->types)
Johnathan Mantey49c53ac2019-05-02 09:22:38 -0700433 {
434 for (const std::string& sensor : *allSensors)
435 {
436 if (boost::starts_with(sensor, type))
437 {
438 activeSensors->emplace(sensor);
439 }
440 }
441 }
442}
443
444/**
Carol Wang4bb3dc32019-10-17 18:15:02 +0800445 * @brief Retrieves valid chassis path
446 * @param asyncResp Pointer to object holding response data
447 * @param callback Callback for next step to get valid chassis path
448 */
449template <typename Callback>
Ed Tanousb5a76932020-09-29 16:16:58 -0700450void getValidChassisPath(const std::shared_ptr<SensorsAsyncResp>& asyncResp,
Carol Wang4bb3dc32019-10-17 18:15:02 +0800451 Callback&& callback)
452{
453 BMCWEB_LOG_DEBUG << "checkChassisId enter";
454 const std::array<const char*, 2> interfaces = {
455 "xyz.openbmc_project.Inventory.Item.Board",
456 "xyz.openbmc_project.Inventory.Item.Chassis"};
457
458 auto respHandler =
459 [callback{std::move(callback)},
460 asyncResp](const boost::system::error_code ec,
461 const std::vector<std::string>& chassisPaths) mutable {
462 BMCWEB_LOG_DEBUG << "getValidChassisPath respHandler enter";
463 if (ec)
464 {
465 BMCWEB_LOG_ERROR
466 << "getValidChassisPath respHandler DBUS error: " << ec;
zhanghch058d1b46d2021-04-01 11:18:24 +0800467 messages::internalError(asyncResp->asyncResp->res);
Carol Wang4bb3dc32019-10-17 18:15:02 +0800468 return;
469 }
470
471 std::optional<std::string> chassisPath;
472 std::string chassisName;
473 for (const std::string& chassis : chassisPaths)
474 {
George Liu28aa8de2021-02-01 15:13:30 +0800475 sdbusplus::message::object_path path(chassis);
476 chassisName = path.filename();
477 if (chassisName.empty())
Carol Wang4bb3dc32019-10-17 18:15:02 +0800478 {
479 BMCWEB_LOG_ERROR << "Failed to find '/' in " << chassis;
480 continue;
481 }
Carol Wang4bb3dc32019-10-17 18:15:02 +0800482 if (chassisName == asyncResp->chassisId)
483 {
484 chassisPath = chassis;
485 break;
486 }
487 }
488 callback(chassisPath);
489 };
490
491 // Get the Chassis Collection
492 crow::connections::systemBus->async_method_call(
493 respHandler, "xyz.openbmc_project.ObjectMapper",
494 "/xyz/openbmc_project/object_mapper",
495 "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths",
496 "/xyz/openbmc_project/inventory", 0, interfaces);
497 BMCWEB_LOG_DEBUG << "checkChassisId exit";
498}
499
500/**
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +0100501 * @brief Retrieves requested chassis sensors and redundancy data from DBus .
Kowalski, Kamil588c3f02018-04-03 14:55:27 +0200502 * @param SensorsAsyncResp Pointer to object holding response data
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +0100503 * @param callback Callback for next step in gathered sensor processing
504 */
505template <typename Callback>
Ed Tanousb5a76932020-09-29 16:16:58 -0700506void getChassis(const std::shared_ptr<SensorsAsyncResp>& sensorsAsyncResp,
Ed Tanous1abe55e2018-09-05 08:30:59 -0700507 Callback&& callback)
508{
509 BMCWEB_LOG_DEBUG << "getChassis enter";
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -0500510 const std::array<const char*, 2> interfaces = {
Johnathan Mantey49c53ac2019-05-02 09:22:38 -0700511 "xyz.openbmc_project.Inventory.Item.Board",
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -0500512 "xyz.openbmc_project.Inventory.Item.Chassis"};
Johnathan Mantey49c53ac2019-05-02 09:22:38 -0700513 auto respHandler = [callback{std::move(callback)}, sensorsAsyncResp](
514 const boost::system::error_code ec,
515 const std::vector<std::string>& chassisPaths) {
Ed Tanous1abe55e2018-09-05 08:30:59 -0700516 BMCWEB_LOG_DEBUG << "getChassis respHandler enter";
517 if (ec)
518 {
519 BMCWEB_LOG_ERROR << "getChassis respHandler DBUS error: " << ec;
zhanghch058d1b46d2021-04-01 11:18:24 +0800520 messages::internalError(sensorsAsyncResp->asyncResp->res);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700521 return;
522 }
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +0100523
Johnathan Mantey49c53ac2019-05-02 09:22:38 -0700524 const std::string* chassisPath = nullptr;
525 std::string chassisName;
526 for (const std::string& chassis : chassisPaths)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700527 {
George Liu28aa8de2021-02-01 15:13:30 +0800528 sdbusplus::message::object_path path(chassis);
529 chassisName = path.filename();
530 if (chassisName.empty())
Ed Tanous1abe55e2018-09-05 08:30:59 -0700531 {
Johnathan Mantey49c53ac2019-05-02 09:22:38 -0700532 BMCWEB_LOG_ERROR << "Failed to find '/' in " << chassis;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700533 continue;
534 }
Johnathan Mantey49c53ac2019-05-02 09:22:38 -0700535 if (chassisName == sensorsAsyncResp->chassisId)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700536 {
Johnathan Mantey49c53ac2019-05-02 09:22:38 -0700537 chassisPath = &chassis;
538 break;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700539 }
Johnathan Mantey49c53ac2019-05-02 09:22:38 -0700540 }
541 if (chassisPath == nullptr)
542 {
zhanghch058d1b46d2021-04-01 11:18:24 +0800543 messages::resourceNotFound(sensorsAsyncResp->asyncResp->res,
544 "Chassis", sensorsAsyncResp->chassisId);
Johnathan Mantey49c53ac2019-05-02 09:22:38 -0700545 return;
546 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700547
Johnathan Mantey49c53ac2019-05-02 09:22:38 -0700548 const std::string& chassisSubNode = sensorsAsyncResp->chassisSubNode;
Adrian Ambrożewicza0ec28b2020-04-10 14:47:28 +0200549 if (chassisSubNode == sensors::node::power)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700550 {
zhanghch058d1b46d2021-04-01 11:18:24 +0800551 sensorsAsyncResp->asyncResp->res.jsonValue["@odata.type"] =
Johnathan Mantey49c53ac2019-05-02 09:22:38 -0700552 "#Power.v1_5_2.Power";
Ed Tanous1abe55e2018-09-05 08:30:59 -0700553 }
Adrian Ambrożewicza0ec28b2020-04-10 14:47:28 +0200554 else if (chassisSubNode == sensors::node::thermal)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700555 {
zhanghch058d1b46d2021-04-01 11:18:24 +0800556 sensorsAsyncResp->asyncResp->res.jsonValue["@odata.type"] =
Johnathan Mantey49c53ac2019-05-02 09:22:38 -0700557 "#Thermal.v1_4_0.Thermal";
zhanghch058d1b46d2021-04-01 11:18:24 +0800558 sensorsAsyncResp->asyncResp->res.jsonValue["Fans"] =
559 nlohmann::json::array();
560 sensorsAsyncResp->asyncResp->res.jsonValue["Temperatures"] =
Jennifer Lee4f9a2132019-03-04 12:45:19 -0800561 nlohmann::json::array();
Ed Tanous1abe55e2018-09-05 08:30:59 -0700562 }
Adrian Ambrożewicza0ec28b2020-04-10 14:47:28 +0200563 else if (chassisSubNode == sensors::node::sensors)
Anthony Wilson95a3eca2019-06-11 10:44:47 -0500564 {
zhanghch058d1b46d2021-04-01 11:18:24 +0800565 sensorsAsyncResp->asyncResp->res.jsonValue["@odata.type"] =
Anthony Wilson95a3eca2019-06-11 10:44:47 -0500566 "#SensorCollection.SensorCollection";
zhanghch058d1b46d2021-04-01 11:18:24 +0800567 sensorsAsyncResp->asyncResp->res.jsonValue["Description"] =
Anthony Wilson95a3eca2019-06-11 10:44:47 -0500568 "Collection of Sensors for this Chassis";
zhanghch058d1b46d2021-04-01 11:18:24 +0800569 sensorsAsyncResp->asyncResp->res.jsonValue["Members"] =
Anthony Wilson95a3eca2019-06-11 10:44:47 -0500570 nlohmann::json::array();
zhanghch058d1b46d2021-04-01 11:18:24 +0800571 sensorsAsyncResp->asyncResp->res.jsonValue["Members@odata.count"] =
572 0;
Anthony Wilson95a3eca2019-06-11 10:44:47 -0500573 }
574
Adrian Ambrożewicza0ec28b2020-04-10 14:47:28 +0200575 if (chassisSubNode != sensors::node::sensors)
Anthony Wilson95a3eca2019-06-11 10:44:47 -0500576 {
zhanghch058d1b46d2021-04-01 11:18:24 +0800577 sensorsAsyncResp->asyncResp->res.jsonValue["Id"] = chassisSubNode;
Anthony Wilson95a3eca2019-06-11 10:44:47 -0500578 }
579
zhanghch058d1b46d2021-04-01 11:18:24 +0800580 sensorsAsyncResp->asyncResp->res.jsonValue["@odata.id"] =
Johnathan Mantey49c53ac2019-05-02 09:22:38 -0700581 "/redfish/v1/Chassis/" + sensorsAsyncResp->chassisId + "/" +
582 chassisSubNode;
zhanghch058d1b46d2021-04-01 11:18:24 +0800583 sensorsAsyncResp->asyncResp->res.jsonValue["Name"] = chassisSubNode;
Shawn McCarney8fb49dd2019-06-12 17:47:00 -0500584 // Get the list of all sensors for this Chassis element
585 std::string sensorPath = *chassisPath + "/all_sensors";
Johnathan Mantey49c53ac2019-05-02 09:22:38 -0700586 crow::connections::systemBus->async_method_call(
587 [sensorsAsyncResp, callback{std::move(callback)}](
Ed Tanous271584a2019-07-09 16:24:22 -0700588 const boost::system::error_code& e,
Johnathan Mantey49c53ac2019-05-02 09:22:38 -0700589 const std::variant<std::vector<std::string>>&
590 variantEndpoints) {
Ed Tanous271584a2019-07-09 16:24:22 -0700591 if (e)
Johnathan Mantey49c53ac2019-05-02 09:22:38 -0700592 {
Ed Tanous271584a2019-07-09 16:24:22 -0700593 if (e.value() != EBADR)
Johnathan Mantey49c53ac2019-05-02 09:22:38 -0700594 {
zhanghch058d1b46d2021-04-01 11:18:24 +0800595 messages::internalError(
596 sensorsAsyncResp->asyncResp->res);
Johnathan Mantey49c53ac2019-05-02 09:22:38 -0700597 return;
598 }
599 }
600 const std::vector<std::string>* nodeSensorList =
601 std::get_if<std::vector<std::string>>(&(variantEndpoints));
602 if (nodeSensorList == nullptr)
603 {
604 messages::resourceNotFound(
zhanghch058d1b46d2021-04-01 11:18:24 +0800605 sensorsAsyncResp->asyncResp->res,
606 sensorsAsyncResp->chassisSubNode,
Adrian Ambrożewicza0ec28b2020-04-10 14:47:28 +0200607 sensorsAsyncResp->chassisSubNode ==
608 sensors::node::thermal
Johnathan Mantey49c53ac2019-05-02 09:22:38 -0700609 ? "Temperatures"
Patrick Williams738c1e62021-02-22 17:14:25 -0600610 : sensorsAsyncResp->chassisSubNode ==
611 sensors::node::power
612 ? "Voltages"
613 : "Sensors");
Johnathan Mantey49c53ac2019-05-02 09:22:38 -0700614 return;
615 }
616 const std::shared_ptr<boost::container::flat_set<std::string>>
617 culledSensorList = std::make_shared<
618 boost::container::flat_set<std::string>>();
619 reduceSensorList(sensorsAsyncResp, nodeSensorList,
620 culledSensorList);
621 callback(culledSensorList);
622 },
623 "xyz.openbmc_project.ObjectMapper", sensorPath,
624 "org.freedesktop.DBus.Properties", "Get",
625 "xyz.openbmc_project.Association", "endpoints");
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +0100626 };
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +0100627
Johnathan Mantey49c53ac2019-05-02 09:22:38 -0700628 // Get the Chassis Collection
Ed Tanous1abe55e2018-09-05 08:30:59 -0700629 crow::connections::systemBus->async_method_call(
Johnathan Mantey49c53ac2019-05-02 09:22:38 -0700630 respHandler, "xyz.openbmc_project.ObjectMapper",
631 "/xyz/openbmc_project/object_mapper",
632 "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths",
Ed Tanous271584a2019-07-09 16:24:22 -0700633 "/xyz/openbmc_project/inventory", 0, interfaces);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700634 BMCWEB_LOG_DEBUG << "getChassis exit";
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +0100635}
636
637/**
Shawn McCarneyde629b62019-03-08 10:42:51 -0600638 * @brief Finds all DBus object paths that implement ObjectManager.
639 *
640 * Creates a mapping from the associated connection name to the object path.
641 *
642 * Finds the object paths asynchronously. Invokes callback when information has
643 * been obtained.
644 *
645 * The callback must have the following signature:
646 * @code
Shawn McCarney8fb49dd2019-06-12 17:47:00 -0500647 * callback(std::shared_ptr<boost::container::flat_map<std::string,
648 * std::string>> objectMgrPaths)
Shawn McCarneyde629b62019-03-08 10:42:51 -0600649 * @endcode
650 *
Johnathan Mantey49c53ac2019-05-02 09:22:38 -0700651 * @param sensorsAsyncResp Pointer to object holding response data.
Shawn McCarneyde629b62019-03-08 10:42:51 -0600652 * @param callback Callback to invoke when object paths obtained.
653 */
654template <typename Callback>
Ed Tanousb5a76932020-09-29 16:16:58 -0700655void getObjectManagerPaths(
Ed Tanous81ce6092020-12-17 16:54:55 +0000656 const std::shared_ptr<SensorsAsyncResp>& sensorsAsyncResp,
Ed Tanousb5a76932020-09-29 16:16:58 -0700657 Callback&& callback)
Shawn McCarneyde629b62019-03-08 10:42:51 -0600658{
659 BMCWEB_LOG_DEBUG << "getObjectManagerPaths enter";
660 const std::array<std::string, 1> interfaces = {
661 "org.freedesktop.DBus.ObjectManager"};
662
663 // Response handler for GetSubTree DBus method
664 auto respHandler = [callback{std::move(callback)},
Ed Tanous81ce6092020-12-17 16:54:55 +0000665 sensorsAsyncResp](const boost::system::error_code ec,
Shawn McCarneyde629b62019-03-08 10:42:51 -0600666 const GetSubTreeType& subtree) {
667 BMCWEB_LOG_DEBUG << "getObjectManagerPaths respHandler enter";
668 if (ec)
669 {
zhanghch058d1b46d2021-04-01 11:18:24 +0800670 messages::internalError(sensorsAsyncResp->asyncResp->res);
Shawn McCarneyde629b62019-03-08 10:42:51 -0600671 BMCWEB_LOG_ERROR << "getObjectManagerPaths respHandler: DBus error "
672 << ec;
673 return;
674 }
675
676 // Loop over returned object paths
Shawn McCarney8fb49dd2019-06-12 17:47:00 -0500677 std::shared_ptr<boost::container::flat_map<std::string, std::string>>
678 objectMgrPaths = std::make_shared<
679 boost::container::flat_map<std::string, std::string>>();
Shawn McCarneyde629b62019-03-08 10:42:51 -0600680 for (const std::pair<
681 std::string,
682 std::vector<std::pair<std::string, std::vector<std::string>>>>&
683 object : subtree)
684 {
685 // Loop over connections for current object path
686 const std::string& objectPath = object.first;
687 for (const std::pair<std::string, std::vector<std::string>>&
688 objData : object.second)
689 {
690 // Add mapping from connection to object path
691 const std::string& connection = objData.first;
Shawn McCarney8fb49dd2019-06-12 17:47:00 -0500692 (*objectMgrPaths)[connection] = objectPath;
Shawn McCarneyde629b62019-03-08 10:42:51 -0600693 BMCWEB_LOG_DEBUG << "Added mapping " << connection << " -> "
694 << objectPath;
695 }
696 }
Shawn McCarney8fb49dd2019-06-12 17:47:00 -0500697 callback(objectMgrPaths);
Shawn McCarneyde629b62019-03-08 10:42:51 -0600698 BMCWEB_LOG_DEBUG << "getObjectManagerPaths respHandler exit";
699 };
700
701 // Query mapper for all DBus object paths that implement ObjectManager
702 crow::connections::systemBus->async_method_call(
703 std::move(respHandler), "xyz.openbmc_project.ObjectMapper",
704 "/xyz/openbmc_project/object_mapper",
Ed Tanous271584a2019-07-09 16:24:22 -0700705 "xyz.openbmc_project.ObjectMapper", "GetSubTree", "/", 0, interfaces);
Shawn McCarneyde629b62019-03-08 10:42:51 -0600706 BMCWEB_LOG_DEBUG << "getObjectManagerPaths exit";
707}
708
709/**
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -0500710 * @brief Returns the Redfish State value for the specified inventory item.
711 * @param inventoryItem D-Bus inventory item associated with a sensor.
712 * @return State value for inventory item.
James Feist34dd1792019-05-17 14:10:54 -0700713 */
Ed Tanous23a21a12020-07-25 04:45:05 +0000714inline std::string getState(const InventoryItem* inventoryItem)
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -0500715{
716 if ((inventoryItem != nullptr) && !(inventoryItem->isPresent))
717 {
718 return "Absent";
719 }
James Feist34dd1792019-05-17 14:10:54 -0700720
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -0500721 return "Enabled";
722}
723
724/**
725 * @brief Returns the Redfish Health value for the specified sensor.
726 * @param sensorJson Sensor JSON object.
727 * @param interfacesDict Map of all sensor interfaces.
728 * @param inventoryItem D-Bus inventory item associated with the sensor. Will
729 * be nullptr if no associated inventory item was found.
730 * @return Health value for sensor.
731 */
Ed Tanous23a21a12020-07-25 04:45:05 +0000732inline std::string getHealth(
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -0500733 nlohmann::json& sensorJson,
James Feist34dd1792019-05-17 14:10:54 -0700734 const boost::container::flat_map<
735 std::string, boost::container::flat_map<std::string, SensorVariant>>&
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -0500736 interfacesDict,
737 const InventoryItem* inventoryItem)
James Feist34dd1792019-05-17 14:10:54 -0700738{
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -0500739 // Get current health value (if any) in the sensor JSON object. Some JSON
740 // objects contain multiple sensors (such as PowerSupplies). We want to set
741 // the overall health to be the most severe of any of the sensors.
742 std::string currentHealth;
743 auto statusIt = sensorJson.find("Status");
744 if (statusIt != sensorJson.end())
745 {
746 auto healthIt = statusIt->find("Health");
747 if (healthIt != statusIt->end())
748 {
749 std::string* health = healthIt->get_ptr<std::string*>();
750 if (health != nullptr)
751 {
752 currentHealth = *health;
753 }
754 }
755 }
756
757 // If current health in JSON object is already Critical, return that. This
758 // should override the sensor health, which might be less severe.
759 if (currentHealth == "Critical")
760 {
761 return "Critical";
762 }
763
764 // Check if sensor has critical threshold alarm
James Feist34dd1792019-05-17 14:10:54 -0700765 auto criticalThresholdIt =
766 interfacesDict.find("xyz.openbmc_project.Sensor.Threshold.Critical");
767 if (criticalThresholdIt != interfacesDict.end())
768 {
769 auto thresholdHighIt =
770 criticalThresholdIt->second.find("CriticalAlarmHigh");
771 auto thresholdLowIt =
772 criticalThresholdIt->second.find("CriticalAlarmLow");
773 if (thresholdHighIt != criticalThresholdIt->second.end())
774 {
775 const bool* asserted = std::get_if<bool>(&thresholdHighIt->second);
776 if (asserted == nullptr)
777 {
778 BMCWEB_LOG_ERROR << "Illegal sensor threshold";
779 }
780 else if (*asserted)
781 {
782 return "Critical";
783 }
784 }
785 if (thresholdLowIt != criticalThresholdIt->second.end())
786 {
787 const bool* asserted = std::get_if<bool>(&thresholdLowIt->second);
788 if (asserted == nullptr)
789 {
790 BMCWEB_LOG_ERROR << "Illegal sensor threshold";
791 }
792 else if (*asserted)
793 {
794 return "Critical";
795 }
796 }
797 }
798
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -0500799 // Check if associated inventory item is not functional
800 if ((inventoryItem != nullptr) && !(inventoryItem->isFunctional))
801 {
802 return "Critical";
803 }
804
805 // If current health in JSON object is already Warning, return that. This
806 // should override the sensor status, which might be less severe.
807 if (currentHealth == "Warning")
808 {
809 return "Warning";
810 }
811
812 // Check if sensor has warning threshold alarm
James Feist34dd1792019-05-17 14:10:54 -0700813 auto warningThresholdIt =
814 interfacesDict.find("xyz.openbmc_project.Sensor.Threshold.Warning");
815 if (warningThresholdIt != interfacesDict.end())
816 {
817 auto thresholdHighIt =
818 warningThresholdIt->second.find("WarningAlarmHigh");
819 auto thresholdLowIt =
820 warningThresholdIt->second.find("WarningAlarmLow");
821 if (thresholdHighIt != warningThresholdIt->second.end())
822 {
823 const bool* asserted = std::get_if<bool>(&thresholdHighIt->second);
824 if (asserted == nullptr)
825 {
826 BMCWEB_LOG_ERROR << "Illegal sensor threshold";
827 }
828 else if (*asserted)
829 {
830 return "Warning";
831 }
832 }
833 if (thresholdLowIt != warningThresholdIt->second.end())
834 {
835 const bool* asserted = std::get_if<bool>(&thresholdLowIt->second);
836 if (asserted == nullptr)
837 {
838 BMCWEB_LOG_ERROR << "Illegal sensor threshold";
839 }
840 else if (*asserted)
841 {
842 return "Warning";
843 }
844 }
845 }
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -0500846
James Feist34dd1792019-05-17 14:10:54 -0700847 return "OK";
848}
849
Ed Tanous23a21a12020-07-25 04:45:05 +0000850inline void setLedState(nlohmann::json& sensorJson,
Anthony Wilsond5005492019-07-31 16:34:17 -0500851 const InventoryItem* inventoryItem)
852{
853 if (inventoryItem != nullptr && !inventoryItem->ledObjectPath.empty())
854 {
855 switch (inventoryItem->ledState)
856 {
857 case LedState::OFF:
858 sensorJson["IndicatorLED"] = "Off";
859 break;
860 case LedState::ON:
861 sensorJson["IndicatorLED"] = "Lit";
862 break;
863 case LedState::BLINK:
864 sensorJson["IndicatorLED"] = "Blinking";
865 break;
Ed Tanous23a21a12020-07-25 04:45:05 +0000866 case LedState::UNKNOWN:
Anthony Wilsond5005492019-07-31 16:34:17 -0500867 break;
868 }
869 }
870}
871
James Feist34dd1792019-05-17 14:10:54 -0700872/**
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +0100873 * @brief Builds a json sensor representation of a sensor.
874 * @param sensorName The name of the sensor to be built
Gunnar Mills274fad52018-06-13 15:45:36 -0500875 * @param sensorType The type (temperature, fan_tach, etc) of the sensor to
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +0100876 * build
Adrian Ambrożewicza0ec28b2020-04-10 14:47:28 +0200877 * @param sensorsAsyncResp Sensor metadata
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +0100878 * @param interfacesDict A dictionary of the interfaces and properties of said
879 * interfaces to be built from
880 * @param sensor_json The json object to fill
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -0500881 * @param inventoryItem D-Bus inventory item associated with the sensor. Will
882 * be nullptr if no associated inventory item was found.
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +0100883 */
Ed Tanous23a21a12020-07-25 04:45:05 +0000884inline void objectInterfacesToJson(
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +0100885 const std::string& sensorName, const std::string& sensorType,
Ed Tanousb5a76932020-09-29 16:16:58 -0700886 const std::shared_ptr<SensorsAsyncResp>& sensorsAsyncResp,
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +0100887 const boost::container::flat_map<
Ed Tanousaa2e59c2018-04-12 12:17:20 -0700888 std::string, boost::container::flat_map<std::string, SensorVariant>>&
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +0100889 interfacesDict,
Ed Tanous81ce6092020-12-17 16:54:55 +0000890 nlohmann::json& sensorJson, InventoryItem* inventoryItem)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700891{
892 // We need a value interface before we can do anything with it
893 auto valueIt = interfacesDict.find("xyz.openbmc_project.Sensor.Value");
894 if (valueIt == interfacesDict.end())
895 {
896 BMCWEB_LOG_ERROR << "Sensor doesn't have a value interface";
897 return;
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +0100898 }
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +0100899
Ed Tanous1abe55e2018-09-05 08:30:59 -0700900 // Assume values exist as is (10^0 == 1) if no scale exists
901 int64_t scaleMultiplier = 0;
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +0100902
Ed Tanous1abe55e2018-09-05 08:30:59 -0700903 auto scaleIt = valueIt->second.find("Scale");
904 // If a scale exists, pull value as int64, and use the scaling.
905 if (scaleIt != valueIt->second.end())
906 {
Ed Tanousabf2add2019-01-22 16:40:12 -0800907 const int64_t* int64Value = std::get_if<int64_t>(&scaleIt->second);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700908 if (int64Value != nullptr)
909 {
910 scaleMultiplier = *int64Value;
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +0100911 }
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +0100912 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700913
Adrian Ambrożewicza0ec28b2020-04-10 14:47:28 +0200914 if (sensorsAsyncResp->chassisSubNode == sensors::node::sensors)
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -0500915 {
Anthony Wilson95a3eca2019-06-11 10:44:47 -0500916 // For sensors in SensorCollection we set Id instead of MemberId,
917 // including power sensors.
Ed Tanous81ce6092020-12-17 16:54:55 +0000918 sensorJson["Id"] = sensorName;
919 sensorJson["Name"] = boost::replace_all_copy(sensorName, "_", " ");
Anthony Wilson95a3eca2019-06-11 10:44:47 -0500920 }
921 else if (sensorType != "power")
922 {
923 // Set MemberId and Name for non-power sensors. For PowerSupplies and
924 // PowerControl, those properties have more general values because
925 // multiple sensors can be stored in the same JSON object.
Ed Tanous81ce6092020-12-17 16:54:55 +0000926 sensorJson["MemberId"] = sensorName;
927 sensorJson["Name"] = boost::replace_all_copy(sensorName, "_", " ");
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -0500928 }
Ed Tanouse742b6c2019-05-03 15:06:53 -0700929
Ed Tanous81ce6092020-12-17 16:54:55 +0000930 sensorJson["Status"]["State"] = getState(inventoryItem);
931 sensorJson["Status"]["Health"] =
932 getHealth(sensorJson, interfacesDict, inventoryItem);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700933
934 // Parameter to set to override the type we get from dbus, and force it to
935 // int, regardless of what is available. This is used for schemas like fan,
936 // that require integers, not floats.
937 bool forceToInt = false;
938
Anthony Wilson3929aca2019-07-19 15:42:33 -0500939 nlohmann::json::json_pointer unit("/Reading");
Adrian Ambrożewicza0ec28b2020-04-10 14:47:28 +0200940 if (sensorsAsyncResp->chassisSubNode == sensors::node::sensors)
Anthony Wilson95a3eca2019-06-11 10:44:47 -0500941 {
Ed Tanous81ce6092020-12-17 16:54:55 +0000942 sensorJson["@odata.type"] = "#Sensor.v1_0_0.Sensor";
Wludzik, Jozefc2bf7f92021-03-08 14:35:54 +0000943
944 const std::string& readingType = sensors::toReadingType(sensorType);
945 if (readingType.empty())
Anthony Wilson95a3eca2019-06-11 10:44:47 -0500946 {
Wludzik, Jozefc2bf7f92021-03-08 14:35:54 +0000947 BMCWEB_LOG_ERROR << "Redfish cannot map reading type for "
948 << sensorType;
Anthony Wilson95a3eca2019-06-11 10:44:47 -0500949 }
Wludzik, Jozefc2bf7f92021-03-08 14:35:54 +0000950 else
Anthony Wilson95a3eca2019-06-11 10:44:47 -0500951 {
Wludzik, Jozefc2bf7f92021-03-08 14:35:54 +0000952 sensorJson["ReadingType"] = readingType;
Anthony Wilson95a3eca2019-06-11 10:44:47 -0500953 }
Wludzik, Jozefc2bf7f92021-03-08 14:35:54 +0000954
955 const std::string& readingUnits = sensors::toReadingUnits(sensorType);
956 if (readingUnits.empty())
Adrian Ambrożewiczf8ede152020-06-02 13:26:33 +0200957 {
Wludzik, Jozefc2bf7f92021-03-08 14:35:54 +0000958 BMCWEB_LOG_ERROR << "Redfish cannot map reading unit for "
959 << sensorType;
960 }
961 else
962 {
963 sensorJson["ReadingUnits"] = readingUnits;
Adrian Ambrożewiczf8ede152020-06-02 13:26:33 +0200964 }
Anthony Wilson95a3eca2019-06-11 10:44:47 -0500965 }
966 else if (sensorType == "temperature")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700967 {
Anthony Wilson3929aca2019-07-19 15:42:33 -0500968 unit = "/ReadingCelsius"_json_pointer;
Ed Tanous81ce6092020-12-17 16:54:55 +0000969 sensorJson["@odata.type"] = "#Thermal.v1_3_0.Temperature";
Ed Tanous1abe55e2018-09-05 08:30:59 -0700970 // TODO(ed) Documentation says that path should be type fan_tach,
971 // implementation seems to implement fan
972 }
973 else if (sensorType == "fan" || sensorType == "fan_tach")
974 {
Anthony Wilson3929aca2019-07-19 15:42:33 -0500975 unit = "/Reading"_json_pointer;
Ed Tanous81ce6092020-12-17 16:54:55 +0000976 sensorJson["ReadingUnits"] = "RPM";
977 sensorJson["@odata.type"] = "#Thermal.v1_3_0.Fan";
978 setLedState(sensorJson, inventoryItem);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700979 forceToInt = true;
980 }
Ed Tanous6f6d0d32018-10-12 11:16:43 -0700981 else if (sensorType == "fan_pwm")
982 {
Anthony Wilson3929aca2019-07-19 15:42:33 -0500983 unit = "/Reading"_json_pointer;
Ed Tanous81ce6092020-12-17 16:54:55 +0000984 sensorJson["ReadingUnits"] = "Percent";
985 sensorJson["@odata.type"] = "#Thermal.v1_3_0.Fan";
986 setLedState(sensorJson, inventoryItem);
Ed Tanous6f6d0d32018-10-12 11:16:43 -0700987 forceToInt = true;
988 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700989 else if (sensorType == "voltage")
990 {
Anthony Wilson3929aca2019-07-19 15:42:33 -0500991 unit = "/ReadingVolts"_json_pointer;
Ed Tanous81ce6092020-12-17 16:54:55 +0000992 sensorJson["@odata.type"] = "#Power.v1_0_0.Voltage";
Ed Tanous1abe55e2018-09-05 08:30:59 -0700993 }
Ed Tanous2474adf2018-09-05 16:31:16 -0700994 else if (sensorType == "power")
995 {
Johnathan Mantey49c53ac2019-05-02 09:22:38 -0700996 std::string sensorNameLower =
997 boost::algorithm::to_lower_copy(sensorName);
998
Eddie James028f7eb2019-05-17 21:24:36 +0000999 if (!sensorName.compare("total_power"))
1000 {
Ed Tanous81ce6092020-12-17 16:54:55 +00001001 sensorJson["@odata.type"] = "#Power.v1_0_0.PowerControl";
Gunnar Mills7ab06f42019-07-02 13:07:16 -05001002 // Put multiple "sensors" into a single PowerControl, so have
1003 // generic names for MemberId and Name. Follows Redfish mockup.
Ed Tanous81ce6092020-12-17 16:54:55 +00001004 sensorJson["MemberId"] = "0";
1005 sensorJson["Name"] = "Chassis Power Control";
Anthony Wilson3929aca2019-07-19 15:42:33 -05001006 unit = "/PowerConsumedWatts"_json_pointer;
Eddie James028f7eb2019-05-17 21:24:36 +00001007 }
1008 else if (sensorNameLower.find("input") != std::string::npos)
Johnathan Mantey49c53ac2019-05-02 09:22:38 -07001009 {
Anthony Wilson3929aca2019-07-19 15:42:33 -05001010 unit = "/PowerInputWatts"_json_pointer;
Johnathan Mantey49c53ac2019-05-02 09:22:38 -07001011 }
1012 else
1013 {
Anthony Wilson3929aca2019-07-19 15:42:33 -05001014 unit = "/PowerOutputWatts"_json_pointer;
Johnathan Mantey49c53ac2019-05-02 09:22:38 -07001015 }
Ed Tanous2474adf2018-09-05 16:31:16 -07001016 }
Ed Tanous1abe55e2018-09-05 08:30:59 -07001017 else
1018 {
1019 BMCWEB_LOG_ERROR << "Redfish cannot map object type for " << sensorName;
1020 return;
1021 }
1022 // Map of dbus interface name, dbus property name and redfish property_name
Anthony Wilson3929aca2019-07-19 15:42:33 -05001023 std::vector<
1024 std::tuple<const char*, const char*, nlohmann::json::json_pointer>>
1025 properties;
Ed Tanous1abe55e2018-09-05 08:30:59 -07001026 properties.reserve(7);
1027
1028 properties.emplace_back("xyz.openbmc_project.Sensor.Value", "Value", unit);
Shawn McCarneyde629b62019-03-08 10:42:51 -06001029
Adrian Ambrożewicza0ec28b2020-04-10 14:47:28 +02001030 if (sensorsAsyncResp->chassisSubNode == sensors::node::sensors)
Anthony Wilson3929aca2019-07-19 15:42:33 -05001031 {
1032 properties.emplace_back(
1033 "xyz.openbmc_project.Sensor.Threshold.Warning", "WarningHigh",
1034 "/Thresholds/UpperCaution/Reading"_json_pointer);
1035 properties.emplace_back(
1036 "xyz.openbmc_project.Sensor.Threshold.Warning", "WarningLow",
1037 "/Thresholds/LowerCaution/Reading"_json_pointer);
1038 properties.emplace_back(
1039 "xyz.openbmc_project.Sensor.Threshold.Critical", "CriticalHigh",
1040 "/Thresholds/UpperCritical/Reading"_json_pointer);
1041 properties.emplace_back(
1042 "xyz.openbmc_project.Sensor.Threshold.Critical", "CriticalLow",
1043 "/Thresholds/LowerCritical/Reading"_json_pointer);
1044 }
1045 else if (sensorType != "power")
Shawn McCarneyde629b62019-03-08 10:42:51 -06001046 {
1047 properties.emplace_back("xyz.openbmc_project.Sensor.Threshold.Warning",
Anthony Wilson3929aca2019-07-19 15:42:33 -05001048 "WarningHigh",
1049 "/UpperThresholdNonCritical"_json_pointer);
Shawn McCarneyde629b62019-03-08 10:42:51 -06001050 properties.emplace_back("xyz.openbmc_project.Sensor.Threshold.Warning",
Anthony Wilson3929aca2019-07-19 15:42:33 -05001051 "WarningLow",
1052 "/LowerThresholdNonCritical"_json_pointer);
Shawn McCarneyde629b62019-03-08 10:42:51 -06001053 properties.emplace_back("xyz.openbmc_project.Sensor.Threshold.Critical",
Anthony Wilson3929aca2019-07-19 15:42:33 -05001054 "CriticalHigh",
1055 "/UpperThresholdCritical"_json_pointer);
Shawn McCarneyde629b62019-03-08 10:42:51 -06001056 properties.emplace_back("xyz.openbmc_project.Sensor.Threshold.Critical",
Anthony Wilson3929aca2019-07-19 15:42:33 -05001057 "CriticalLow",
1058 "/LowerThresholdCritical"_json_pointer);
Shawn McCarneyde629b62019-03-08 10:42:51 -06001059 }
Ed Tanous1abe55e2018-09-05 08:30:59 -07001060
Ed Tanous2474adf2018-09-05 16:31:16 -07001061 // TODO Need to get UpperThresholdFatal and LowerThresholdFatal
1062
Adrian Ambrożewicza0ec28b2020-04-10 14:47:28 +02001063 if (sensorsAsyncResp->chassisSubNode == sensors::node::sensors)
Anthony Wilson95a3eca2019-06-11 10:44:47 -05001064 {
1065 properties.emplace_back("xyz.openbmc_project.Sensor.Value", "MinValue",
Anthony Wilson3929aca2019-07-19 15:42:33 -05001066 "/ReadingRangeMin"_json_pointer);
Anthony Wilson95a3eca2019-06-11 10:44:47 -05001067 properties.emplace_back("xyz.openbmc_project.Sensor.Value", "MaxValue",
Anthony Wilson3929aca2019-07-19 15:42:33 -05001068 "/ReadingRangeMax"_json_pointer);
Anthony Wilson95a3eca2019-06-11 10:44:47 -05001069 }
1070 else if (sensorType == "temperature")
Ed Tanous1abe55e2018-09-05 08:30:59 -07001071 {
1072 properties.emplace_back("xyz.openbmc_project.Sensor.Value", "MinValue",
Anthony Wilson3929aca2019-07-19 15:42:33 -05001073 "/MinReadingRangeTemp"_json_pointer);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001074 properties.emplace_back("xyz.openbmc_project.Sensor.Value", "MaxValue",
Anthony Wilson3929aca2019-07-19 15:42:33 -05001075 "/MaxReadingRangeTemp"_json_pointer);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001076 }
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001077 else if (sensorType != "power")
Ed Tanous1abe55e2018-09-05 08:30:59 -07001078 {
1079 properties.emplace_back("xyz.openbmc_project.Sensor.Value", "MinValue",
Anthony Wilson3929aca2019-07-19 15:42:33 -05001080 "/MinReadingRange"_json_pointer);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001081 properties.emplace_back("xyz.openbmc_project.Sensor.Value", "MaxValue",
Anthony Wilson3929aca2019-07-19 15:42:33 -05001082 "/MaxReadingRange"_json_pointer);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001083 }
1084
Anthony Wilson3929aca2019-07-19 15:42:33 -05001085 for (const std::tuple<const char*, const char*,
1086 nlohmann::json::json_pointer>& p : properties)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001087 {
1088 auto interfaceProperties = interfacesDict.find(std::get<0>(p));
1089 if (interfaceProperties != interfacesDict.end())
1090 {
Ed Tanous271584a2019-07-09 16:24:22 -07001091 auto thisValueIt = interfaceProperties->second.find(std::get<1>(p));
1092 if (thisValueIt != interfaceProperties->second.end())
Ed Tanous1abe55e2018-09-05 08:30:59 -07001093 {
Ed Tanous271584a2019-07-09 16:24:22 -07001094 const SensorVariant& valueVariant = thisValueIt->second;
Anthony Wilson3929aca2019-07-19 15:42:33 -05001095
1096 // The property we want to set may be nested json, so use
1097 // a json_pointer for easy indexing into the json structure.
1098 const nlohmann::json::json_pointer& key = std::get<2>(p);
1099
Ed Tanous1abe55e2018-09-05 08:30:59 -07001100 // Attempt to pull the int64 directly
Ed Tanousabf2add2019-01-22 16:40:12 -08001101 const int64_t* int64Value = std::get_if<int64_t>(&valueVariant);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001102
Ed Tanousabf2add2019-01-22 16:40:12 -08001103 const double* doubleValue = std::get_if<double>(&valueVariant);
Eddie James028f7eb2019-05-17 21:24:36 +00001104 const uint32_t* uValue = std::get_if<uint32_t>(&valueVariant);
Ed Tanous6f6d0d32018-10-12 11:16:43 -07001105 double temp = 0.0;
1106 if (int64Value != nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001107 {
Ed Tanous271584a2019-07-09 16:24:22 -07001108 temp = static_cast<double>(*int64Value);
Ed Tanous6f6d0d32018-10-12 11:16:43 -07001109 }
1110 else if (doubleValue != nullptr)
1111 {
1112 temp = *doubleValue;
1113 }
Eddie James028f7eb2019-05-17 21:24:36 +00001114 else if (uValue != nullptr)
1115 {
1116 temp = *uValue;
1117 }
Ed Tanous6f6d0d32018-10-12 11:16:43 -07001118 else
1119 {
1120 BMCWEB_LOG_ERROR
1121 << "Got value interface that wasn't int or double";
1122 continue;
1123 }
1124 temp = temp * std::pow(10, scaleMultiplier);
1125 if (forceToInt)
1126 {
Ed Tanous81ce6092020-12-17 16:54:55 +00001127 sensorJson[key] = static_cast<int64_t>(temp);
Ed Tanous6f6d0d32018-10-12 11:16:43 -07001128 }
1129 else
1130 {
Ed Tanous81ce6092020-12-17 16:54:55 +00001131 sensorJson[key] = temp;
Ed Tanous1abe55e2018-09-05 08:30:59 -07001132 }
1133 }
1134 }
1135 }
Adrian Ambrożewicza0ec28b2020-04-10 14:47:28 +02001136
Ed Tanous81ce6092020-12-17 16:54:55 +00001137 sensorsAsyncResp->addMetadata(sensorJson, unit.to_string(),
Adrian Ambrożewicza0ec28b2020-04-10 14:47:28 +02001138 "/xyz/openbmc_project/sensors/" + sensorType +
1139 "/" + sensorName);
1140
Ed Tanous1abe55e2018-09-05 08:30:59 -07001141 BMCWEB_LOG_DEBUG << "Added sensor " << sensorName;
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +01001142}
1143
Ed Tanousb5a76932020-09-29 16:16:58 -07001144inline void populateFanRedundancy(
1145 const std::shared_ptr<SensorsAsyncResp>& sensorsAsyncResp)
James Feist8bd25cc2019-03-15 15:14:00 -07001146{
1147 crow::connections::systemBus->async_method_call(
1148 [sensorsAsyncResp](const boost::system::error_code ec,
1149 const GetSubTreeType& resp) {
1150 if (ec)
1151 {
1152 return; // don't have to have this interface
1153 }
Ed Tanouse278c182019-03-13 16:23:37 -07001154 for (const std::pair<std::string,
1155 std::vector<std::pair<
1156 std::string, std::vector<std::string>>>>&
1157 pathPair : resp)
James Feist8bd25cc2019-03-15 15:14:00 -07001158 {
Ed Tanouse278c182019-03-13 16:23:37 -07001159 const std::string& path = pathPair.first;
1160 const std::vector<
1161 std::pair<std::string, std::vector<std::string>>>& objDict =
1162 pathPair.second;
James Feist8bd25cc2019-03-15 15:14:00 -07001163 if (objDict.empty())
1164 {
1165 continue; // this should be impossible
1166 }
1167
1168 const std::string& owner = objDict.begin()->first;
1169 crow::connections::systemBus->async_method_call(
1170 [path, owner,
Ed Tanous271584a2019-07-09 16:24:22 -07001171 sensorsAsyncResp](const boost::system::error_code e,
James Feist8bd25cc2019-03-15 15:14:00 -07001172 std::variant<std::vector<std::string>>
1173 variantEndpoints) {
Ed Tanous271584a2019-07-09 16:24:22 -07001174 if (e)
James Feist8bd25cc2019-03-15 15:14:00 -07001175 {
1176 return; // if they don't have an association we
1177 // can't tell what chassis is
1178 }
1179 // verify part of the right chassis
1180 auto endpoints = std::get_if<std::vector<std::string>>(
1181 &variantEndpoints);
1182
1183 if (endpoints == nullptr)
1184 {
1185 BMCWEB_LOG_ERROR << "Invalid association interface";
zhanghch058d1b46d2021-04-01 11:18:24 +08001186 messages::internalError(
1187 sensorsAsyncResp->asyncResp->res);
James Feist8bd25cc2019-03-15 15:14:00 -07001188 return;
1189 }
1190
1191 auto found = std::find_if(
1192 endpoints->begin(), endpoints->end(),
1193 [sensorsAsyncResp](const std::string& entry) {
1194 return entry.find(
1195 sensorsAsyncResp->chassisId) !=
1196 std::string::npos;
1197 });
1198
1199 if (found == endpoints->end())
1200 {
1201 return;
1202 }
1203 crow::connections::systemBus->async_method_call(
1204 [path, sensorsAsyncResp](
Ed Tanous271584a2019-07-09 16:24:22 -07001205 const boost::system::error_code& err,
James Feist8bd25cc2019-03-15 15:14:00 -07001206 const boost::container::flat_map<
1207 std::string,
1208 std::variant<uint8_t,
1209 std::vector<std::string>,
1210 std::string>>& ret) {
Ed Tanous271584a2019-07-09 16:24:22 -07001211 if (err)
James Feist8bd25cc2019-03-15 15:14:00 -07001212 {
1213 return; // don't have to have this
1214 // interface
1215 }
1216 auto findFailures = ret.find("AllowedFailures");
1217 auto findCollection = ret.find("Collection");
1218 auto findStatus = ret.find("Status");
1219
1220 if (findFailures == ret.end() ||
1221 findCollection == ret.end() ||
1222 findStatus == ret.end())
1223 {
1224 BMCWEB_LOG_ERROR
1225 << "Invalid redundancy interface";
1226 messages::internalError(
zhanghch058d1b46d2021-04-01 11:18:24 +08001227 sensorsAsyncResp->asyncResp->res);
James Feist8bd25cc2019-03-15 15:14:00 -07001228 return;
1229 }
1230
1231 auto allowedFailures = std::get_if<uint8_t>(
1232 &(findFailures->second));
1233 auto collection =
1234 std::get_if<std::vector<std::string>>(
1235 &(findCollection->second));
1236 auto status = std::get_if<std::string>(
1237 &(findStatus->second));
1238
1239 if (allowedFailures == nullptr ||
1240 collection == nullptr || status == nullptr)
1241 {
1242
1243 BMCWEB_LOG_ERROR
1244 << "Invalid redundancy interface "
1245 "types";
1246 messages::internalError(
zhanghch058d1b46d2021-04-01 11:18:24 +08001247 sensorsAsyncResp->asyncResp->res);
James Feist8bd25cc2019-03-15 15:14:00 -07001248 return;
1249 }
George Liu28aa8de2021-02-01 15:13:30 +08001250 sdbusplus::message::object_path objectPath(
1251 path);
1252 std::string name = objectPath.filename();
1253 if (name.empty())
James Feist8bd25cc2019-03-15 15:14:00 -07001254 {
1255 // this should be impossible
1256 messages::internalError(
zhanghch058d1b46d2021-04-01 11:18:24 +08001257 sensorsAsyncResp->asyncResp->res);
James Feist8bd25cc2019-03-15 15:14:00 -07001258 return;
1259 }
James Feist8bd25cc2019-03-15 15:14:00 -07001260 std::replace(name.begin(), name.end(), '_',
1261 ' ');
1262
1263 std::string health;
1264
1265 if (boost::ends_with(*status, "Full"))
1266 {
1267 health = "OK";
1268 }
1269 else if (boost::ends_with(*status, "Degraded"))
1270 {
1271 health = "Warning";
1272 }
1273 else
1274 {
1275 health = "Critical";
1276 }
1277 std::vector<nlohmann::json> redfishCollection;
1278 const auto& fanRedfish =
zhanghch058d1b46d2021-04-01 11:18:24 +08001279 sensorsAsyncResp->asyncResp->res
1280 .jsonValue["Fans"];
James Feist8bd25cc2019-03-15 15:14:00 -07001281 for (const std::string& item : *collection)
1282 {
George Liu28aa8de2021-02-01 15:13:30 +08001283 sdbusplus::message::object_path path(item);
1284 std::string itemName = path.filename();
1285 if (itemName.empty())
1286 {
1287 continue;
1288 }
James Feist8bd25cc2019-03-15 15:14:00 -07001289 /*
1290 todo(ed): merge patch that fixes the names
1291 std::replace(itemName.begin(),
1292 itemName.end(), '_', ' ');*/
1293 auto schemaItem = std::find_if(
1294 fanRedfish.begin(), fanRedfish.end(),
1295 [itemName](const nlohmann::json& fan) {
1296 return fan["MemberId"] == itemName;
1297 });
1298 if (schemaItem != fanRedfish.end())
1299 {
1300 redfishCollection.push_back(
1301 {{"@odata.id",
1302 (*schemaItem)["@odata.id"]}});
1303 }
1304 else
1305 {
1306 BMCWEB_LOG_ERROR
1307 << "failed to find fan in schema";
1308 messages::internalError(
zhanghch058d1b46d2021-04-01 11:18:24 +08001309 sensorsAsyncResp->asyncResp->res);
James Feist8bd25cc2019-03-15 15:14:00 -07001310 return;
1311 }
1312 }
1313
Kuiying Wang3e9e72e2020-07-07 10:18:32 +08001314 size_t minNumNeeded =
1315 collection->size() > 0
1316 ? collection->size() - *allowedFailures
1317 : 0;
Ed Tanous271584a2019-07-09 16:24:22 -07001318 nlohmann::json& jResp =
zhanghch058d1b46d2021-04-01 11:18:24 +08001319 sensorsAsyncResp->asyncResp->res
Ed Tanous271584a2019-07-09 16:24:22 -07001320 .jsonValue["Redundancy"];
1321 jResp.push_back(
James Feist8bd25cc2019-03-15 15:14:00 -07001322 {{"@odata.id",
AppaRao Puli717794d2019-10-18 22:54:53 +05301323 "/redfish/v1/Chassis/" +
James Feist8bd25cc2019-03-15 15:14:00 -07001324 sensorsAsyncResp->chassisId + "/" +
1325 sensorsAsyncResp->chassisSubNode +
1326 "#/Redundancy/" +
Ed Tanous271584a2019-07-09 16:24:22 -07001327 std::to_string(jResp.size())},
James Feist8bd25cc2019-03-15 15:14:00 -07001328 {"@odata.type",
1329 "#Redundancy.v1_3_2.Redundancy"},
Kuiying Wang3e9e72e2020-07-07 10:18:32 +08001330 {"MinNumNeeded", minNumNeeded},
James Feist8bd25cc2019-03-15 15:14:00 -07001331 {"MemberId", name},
1332 {"Mode", "N+m"},
1333 {"Name", name},
1334 {"RedundancySet", redfishCollection},
1335 {"Status",
1336 {{"Health", health},
1337 {"State", "Enabled"}}}});
1338 },
1339 owner, path, "org.freedesktop.DBus.Properties",
1340 "GetAll",
1341 "xyz.openbmc_project.Control.FanRedundancy");
1342 },
James Feist02e92e32019-06-26 12:07:05 -07001343 "xyz.openbmc_project.ObjectMapper", path + "/chassis",
James Feist8bd25cc2019-03-15 15:14:00 -07001344 "org.freedesktop.DBus.Properties", "Get",
1345 "xyz.openbmc_project.Association", "endpoints");
1346 }
1347 },
1348 "xyz.openbmc_project.ObjectMapper",
1349 "/xyz/openbmc_project/object_mapper",
1350 "xyz.openbmc_project.ObjectMapper", "GetSubTree",
1351 "/xyz/openbmc_project/control", 2,
1352 std::array<const char*, 1>{
1353 "xyz.openbmc_project.Control.FanRedundancy"});
1354}
1355
Ed Tanousb5a76932020-09-29 16:16:58 -07001356inline void
Ed Tanous81ce6092020-12-17 16:54:55 +00001357 sortJSONResponse(const std::shared_ptr<SensorsAsyncResp>& sensorsAsyncResp)
Johnathan Mantey49c53ac2019-05-02 09:22:38 -07001358{
zhanghch058d1b46d2021-04-01 11:18:24 +08001359 nlohmann::json& response = sensorsAsyncResp->asyncResp->res.jsonValue;
Johnathan Mantey49c53ac2019-05-02 09:22:38 -07001360 std::array<std::string, 2> sensorHeaders{"Temperatures", "Fans"};
Ed Tanous81ce6092020-12-17 16:54:55 +00001361 if (sensorsAsyncResp->chassisSubNode == sensors::node::power)
Johnathan Mantey49c53ac2019-05-02 09:22:38 -07001362 {
1363 sensorHeaders = {"Voltages", "PowerSupplies"};
1364 }
1365 for (const std::string& sensorGroup : sensorHeaders)
1366 {
1367 nlohmann::json::iterator entry = response.find(sensorGroup);
1368 if (entry != response.end())
1369 {
1370 std::sort(entry->begin(), entry->end(),
1371 [](nlohmann::json& c1, nlohmann::json& c2) {
1372 return c1["Name"] < c2["Name"];
1373 });
1374
1375 // add the index counts to the end of each entry
1376 size_t count = 0;
1377 for (nlohmann::json& sensorJson : *entry)
1378 {
1379 nlohmann::json::iterator odata = sensorJson.find("@odata.id");
1380 if (odata == sensorJson.end())
1381 {
1382 continue;
1383 }
1384 std::string* value = odata->get_ptr<std::string*>();
1385 if (value != nullptr)
1386 {
1387 *value += std::to_string(count);
1388 count++;
Ed Tanous81ce6092020-12-17 16:54:55 +00001389 sensorsAsyncResp->updateUri(sensorJson["Name"], *value);
Johnathan Mantey49c53ac2019-05-02 09:22:38 -07001390 }
1391 }
1392 }
1393 }
1394}
1395
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +01001396/**
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001397 * @brief Finds the inventory item with the specified object path.
1398 * @param inventoryItems D-Bus inventory items associated with sensors.
1399 * @param invItemObjPath D-Bus object path of inventory item.
1400 * @return Inventory item within vector, or nullptr if no match found.
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001401 */
Ed Tanous23a21a12020-07-25 04:45:05 +00001402inline InventoryItem* findInventoryItem(
Ed Tanousb5a76932020-09-29 16:16:58 -07001403 const std::shared_ptr<std::vector<InventoryItem>>& inventoryItems,
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001404 const std::string& invItemObjPath)
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001405{
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001406 for (InventoryItem& inventoryItem : *inventoryItems)
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001407 {
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001408 if (inventoryItem.objectPath == invItemObjPath)
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001409 {
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001410 return &inventoryItem;
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001411 }
1412 }
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001413 return nullptr;
1414}
1415
1416/**
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001417 * @brief Finds the inventory item associated with the specified sensor.
1418 * @param inventoryItems D-Bus inventory items associated with sensors.
1419 * @param sensorObjPath D-Bus object path of sensor.
1420 * @return Inventory item within vector, or nullptr if no match found.
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001421 */
Ed Tanous23a21a12020-07-25 04:45:05 +00001422inline InventoryItem* findInventoryItemForSensor(
Ed Tanousb5a76932020-09-29 16:16:58 -07001423 const std::shared_ptr<std::vector<InventoryItem>>& inventoryItems,
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001424 const std::string& sensorObjPath)
1425{
1426 for (InventoryItem& inventoryItem : *inventoryItems)
1427 {
1428 if (inventoryItem.sensors.count(sensorObjPath) > 0)
1429 {
1430 return &inventoryItem;
1431 }
1432 }
1433 return nullptr;
1434}
1435
1436/**
Anthony Wilsond5005492019-07-31 16:34:17 -05001437 * @brief Finds the inventory item associated with the specified led path.
1438 * @param inventoryItems D-Bus inventory items associated with sensors.
1439 * @param ledObjPath D-Bus object path of led.
1440 * @return Inventory item within vector, or nullptr if no match found.
1441 */
1442inline InventoryItem*
1443 findInventoryItemForLed(std::vector<InventoryItem>& inventoryItems,
1444 const std::string& ledObjPath)
1445{
1446 for (InventoryItem& inventoryItem : inventoryItems)
1447 {
1448 if (inventoryItem.ledObjectPath == ledObjPath)
1449 {
1450 return &inventoryItem;
1451 }
1452 }
1453 return nullptr;
1454}
1455
1456/**
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001457 * @brief Adds inventory item and associated sensor to specified vector.
1458 *
1459 * Adds a new InventoryItem to the vector if necessary. Searches for an
1460 * existing InventoryItem with the specified object path. If not found, one is
1461 * added to the vector.
1462 *
1463 * Next, the specified sensor is added to the set of sensors associated with the
1464 * InventoryItem.
1465 *
1466 * @param inventoryItems D-Bus inventory items associated with sensors.
1467 * @param invItemObjPath D-Bus object path of inventory item.
1468 * @param sensorObjPath D-Bus object path of sensor
1469 */
Ed Tanousb5a76932020-09-29 16:16:58 -07001470inline void addInventoryItem(
1471 const std::shared_ptr<std::vector<InventoryItem>>& inventoryItems,
1472 const std::string& invItemObjPath, const std::string& sensorObjPath)
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001473{
1474 // Look for inventory item in vector
1475 InventoryItem* inventoryItem =
1476 findInventoryItem(inventoryItems, invItemObjPath);
1477
1478 // If inventory item doesn't exist in vector, add it
1479 if (inventoryItem == nullptr)
1480 {
1481 inventoryItems->emplace_back(invItemObjPath);
1482 inventoryItem = &(inventoryItems->back());
1483 }
1484
1485 // Add sensor to set of sensors associated with inventory item
1486 inventoryItem->sensors.emplace(sensorObjPath);
1487}
1488
1489/**
1490 * @brief Stores D-Bus data in the specified inventory item.
1491 *
1492 * Finds D-Bus data in the specified map of interfaces. Stores the data in the
1493 * specified InventoryItem.
1494 *
1495 * This data is later used to provide sensor property values in the JSON
1496 * response.
1497 *
1498 * @param inventoryItem Inventory item where data will be stored.
1499 * @param interfacesDict Map containing D-Bus interfaces and their properties
1500 * for the specified inventory item.
1501 */
Ed Tanous23a21a12020-07-25 04:45:05 +00001502inline void storeInventoryItemData(
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001503 InventoryItem& inventoryItem,
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001504 const boost::container::flat_map<
1505 std::string, boost::container::flat_map<std::string, SensorVariant>>&
1506 interfacesDict)
1507{
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001508 // Get properties from Inventory.Item interface
1509 auto interfaceIt =
1510 interfacesDict.find("xyz.openbmc_project.Inventory.Item");
1511 if (interfaceIt != interfacesDict.end())
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001512 {
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001513 auto propertyIt = interfaceIt->second.find("Present");
1514 if (propertyIt != interfaceIt->second.end())
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001515 {
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001516 const bool* value = std::get_if<bool>(&propertyIt->second);
1517 if (value != nullptr)
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001518 {
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001519 inventoryItem.isPresent = *value;
1520 }
1521 }
1522 }
1523
1524 // Check if Inventory.Item.PowerSupply interface is present
1525 interfaceIt =
1526 interfacesDict.find("xyz.openbmc_project.Inventory.Item.PowerSupply");
1527 if (interfaceIt != interfacesDict.end())
1528 {
1529 inventoryItem.isPowerSupply = true;
1530 }
1531
1532 // Get properties from Inventory.Decorator.Asset interface
1533 interfaceIt =
1534 interfacesDict.find("xyz.openbmc_project.Inventory.Decorator.Asset");
1535 if (interfaceIt != interfacesDict.end())
1536 {
1537 auto propertyIt = interfaceIt->second.find("Manufacturer");
1538 if (propertyIt != interfaceIt->second.end())
1539 {
1540 const std::string* value =
1541 std::get_if<std::string>(&propertyIt->second);
1542 if (value != nullptr)
1543 {
1544 inventoryItem.manufacturer = *value;
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001545 }
1546 }
1547
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001548 propertyIt = interfaceIt->second.find("Model");
1549 if (propertyIt != interfaceIt->second.end())
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001550 {
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001551 const std::string* value =
1552 std::get_if<std::string>(&propertyIt->second);
1553 if (value != nullptr)
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001554 {
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001555 inventoryItem.model = *value;
1556 }
1557 }
1558
1559 propertyIt = interfaceIt->second.find("PartNumber");
1560 if (propertyIt != interfaceIt->second.end())
1561 {
1562 const std::string* value =
1563 std::get_if<std::string>(&propertyIt->second);
1564 if (value != nullptr)
1565 {
1566 inventoryItem.partNumber = *value;
1567 }
1568 }
1569
1570 propertyIt = interfaceIt->second.find("SerialNumber");
1571 if (propertyIt != interfaceIt->second.end())
1572 {
1573 const std::string* value =
1574 std::get_if<std::string>(&propertyIt->second);
1575 if (value != nullptr)
1576 {
1577 inventoryItem.serialNumber = *value;
1578 }
1579 }
1580 }
1581
1582 // Get properties from State.Decorator.OperationalStatus interface
1583 interfaceIt = interfacesDict.find(
1584 "xyz.openbmc_project.State.Decorator.OperationalStatus");
1585 if (interfaceIt != interfacesDict.end())
1586 {
1587 auto propertyIt = interfaceIt->second.find("Functional");
1588 if (propertyIt != interfaceIt->second.end())
1589 {
1590 const bool* value = std::get_if<bool>(&propertyIt->second);
1591 if (value != nullptr)
1592 {
1593 inventoryItem.isFunctional = *value;
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001594 }
1595 }
1596 }
1597}
1598
1599/**
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001600 * @brief Gets D-Bus data for inventory items associated with sensors.
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001601 *
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001602 * Uses the specified connections (services) to obtain D-Bus data for inventory
1603 * items associated with sensors. Stores the resulting data in the
1604 * inventoryItems vector.
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001605 *
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001606 * This data is later used to provide sensor property values in the JSON
1607 * response.
1608 *
1609 * Finds the inventory item data asynchronously. Invokes callback when data has
1610 * been obtained.
1611 *
1612 * The callback must have the following signature:
1613 * @code
Anthony Wilsond5005492019-07-31 16:34:17 -05001614 * callback(void)
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001615 * @endcode
1616 *
1617 * This function is called recursively, obtaining data asynchronously from one
1618 * connection in each call. This ensures the callback is not invoked until the
1619 * last asynchronous function has completed.
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001620 *
1621 * @param sensorsAsyncResp Pointer to object holding response data.
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001622 * @param inventoryItems D-Bus inventory items associated with sensors.
1623 * @param invConnections Connections that provide data for the inventory items.
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001624 * @param objectMgrPaths Mappings from connection name to DBus object path that
1625 * implements ObjectManager.
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001626 * @param callback Callback to invoke when inventory data has been obtained.
1627 * @param invConnectionsIndex Current index in invConnections. Only specified
1628 * in recursive calls to this function.
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001629 */
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001630template <typename Callback>
1631static void getInventoryItemsData(
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001632 std::shared_ptr<SensorsAsyncResp> sensorsAsyncResp,
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001633 std::shared_ptr<std::vector<InventoryItem>> inventoryItems,
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001634 std::shared_ptr<boost::container::flat_set<std::string>> invConnections,
1635 std::shared_ptr<boost::container::flat_map<std::string, std::string>>
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001636 objectMgrPaths,
Ed Tanous271584a2019-07-09 16:24:22 -07001637 Callback&& callback, size_t invConnectionsIndex = 0)
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001638{
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001639 BMCWEB_LOG_DEBUG << "getInventoryItemsData enter";
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001640
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001641 // If no more connections left, call callback
1642 if (invConnectionsIndex >= invConnections->size())
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001643 {
Anthony Wilsond5005492019-07-31 16:34:17 -05001644 callback();
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001645 BMCWEB_LOG_DEBUG << "getInventoryItemsData exit";
1646 return;
1647 }
1648
1649 // Get inventory item data from current connection
1650 auto it = invConnections->nth(invConnectionsIndex);
1651 if (it != invConnections->end())
1652 {
1653 const std::string& invConnection = *it;
1654
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001655 // Response handler for GetManagedObjects
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001656 auto respHandler = [sensorsAsyncResp, inventoryItems, invConnections,
1657 objectMgrPaths, callback{std::move(callback)},
1658 invConnectionsIndex](
1659 const boost::system::error_code ec,
1660 ManagedObjectsVectorType& resp) {
1661 BMCWEB_LOG_DEBUG << "getInventoryItemsData respHandler enter";
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001662 if (ec)
1663 {
1664 BMCWEB_LOG_ERROR
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001665 << "getInventoryItemsData respHandler DBus error " << ec;
zhanghch058d1b46d2021-04-01 11:18:24 +08001666 messages::internalError(sensorsAsyncResp->asyncResp->res);
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001667 return;
1668 }
1669
1670 // Loop through returned object paths
1671 for (const auto& objDictEntry : resp)
1672 {
1673 const std::string& objPath =
1674 static_cast<const std::string&>(objDictEntry.first);
1675
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001676 // If this object path is one of the specified inventory items
1677 InventoryItem* inventoryItem =
1678 findInventoryItem(inventoryItems, objPath);
1679 if (inventoryItem != nullptr)
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001680 {
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001681 // Store inventory data in InventoryItem
1682 storeInventoryItemData(*inventoryItem, objDictEntry.second);
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001683 }
1684 }
1685
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001686 // Recurse to get inventory item data from next connection
1687 getInventoryItemsData(sensorsAsyncResp, inventoryItems,
1688 invConnections, objectMgrPaths,
1689 std::move(callback), invConnectionsIndex + 1);
1690
1691 BMCWEB_LOG_DEBUG << "getInventoryItemsData respHandler exit";
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001692 };
1693
1694 // Find DBus object path that implements ObjectManager for the current
1695 // connection. If no mapping found, default to "/".
1696 auto iter = objectMgrPaths->find(invConnection);
1697 const std::string& objectMgrPath =
1698 (iter != objectMgrPaths->end()) ? iter->second : "/";
1699 BMCWEB_LOG_DEBUG << "ObjectManager path for " << invConnection << " is "
1700 << objectMgrPath;
1701
1702 // Get all object paths and their interfaces for current connection
1703 crow::connections::systemBus->async_method_call(
1704 std::move(respHandler), invConnection, objectMgrPath,
1705 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
1706 }
1707
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001708 BMCWEB_LOG_DEBUG << "getInventoryItemsData exit";
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001709}
1710
1711/**
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001712 * @brief Gets connections that provide D-Bus data for inventory items.
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001713 *
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001714 * Gets the D-Bus connections (services) that provide data for the inventory
1715 * items that are associated with sensors.
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001716 *
1717 * Finds the connections asynchronously. Invokes callback when information has
1718 * been obtained.
1719 *
1720 * The callback must have the following signature:
1721 * @code
1722 * callback(std::shared_ptr<boost::container::flat_set<std::string>>
1723 * invConnections)
1724 * @endcode
1725 *
1726 * @param sensorsAsyncResp Pointer to object holding response data.
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001727 * @param inventoryItems D-Bus inventory items associated with sensors.
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001728 * @param callback Callback to invoke when connections have been obtained.
1729 */
1730template <typename Callback>
1731static void getInventoryItemsConnections(
Ed Tanousb5a76932020-09-29 16:16:58 -07001732 const std::shared_ptr<SensorsAsyncResp>& sensorsAsyncResp,
1733 const std::shared_ptr<std::vector<InventoryItem>>& inventoryItems,
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001734 Callback&& callback)
1735{
1736 BMCWEB_LOG_DEBUG << "getInventoryItemsConnections enter";
1737
1738 const std::string path = "/xyz/openbmc_project/inventory";
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001739 const std::array<std::string, 4> interfaces = {
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001740 "xyz.openbmc_project.Inventory.Item",
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001741 "xyz.openbmc_project.Inventory.Item.PowerSupply",
1742 "xyz.openbmc_project.Inventory.Decorator.Asset",
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001743 "xyz.openbmc_project.State.Decorator.OperationalStatus"};
1744
1745 // Response handler for parsing output from GetSubTree
1746 auto respHandler = [callback{std::move(callback)}, sensorsAsyncResp,
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001747 inventoryItems](const boost::system::error_code ec,
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001748 const GetSubTreeType& subtree) {
1749 BMCWEB_LOG_DEBUG << "getInventoryItemsConnections respHandler enter";
1750 if (ec)
1751 {
zhanghch058d1b46d2021-04-01 11:18:24 +08001752 messages::internalError(sensorsAsyncResp->asyncResp->res);
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001753 BMCWEB_LOG_ERROR
1754 << "getInventoryItemsConnections respHandler DBus error " << ec;
1755 return;
1756 }
1757
1758 // Make unique list of connections for desired inventory items
1759 std::shared_ptr<boost::container::flat_set<std::string>>
1760 invConnections =
1761 std::make_shared<boost::container::flat_set<std::string>>();
1762 invConnections->reserve(8);
1763
1764 // Loop through objects from GetSubTree
1765 for (const std::pair<
1766 std::string,
1767 std::vector<std::pair<std::string, std::vector<std::string>>>>&
1768 object : subtree)
1769 {
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001770 // Check if object path is one of the specified inventory items
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001771 const std::string& objPath = object.first;
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001772 if (findInventoryItem(inventoryItems, objPath) != nullptr)
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001773 {
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001774 // Store all connections to inventory item
1775 for (const std::pair<std::string, std::vector<std::string>>&
1776 objData : object.second)
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001777 {
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001778 const std::string& invConnection = objData.first;
1779 invConnections->insert(invConnection);
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001780 }
1781 }
1782 }
Anthony Wilsond5005492019-07-31 16:34:17 -05001783
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001784 callback(invConnections);
1785 BMCWEB_LOG_DEBUG << "getInventoryItemsConnections respHandler exit";
1786 };
1787
1788 // Make call to ObjectMapper to find all inventory items
1789 crow::connections::systemBus->async_method_call(
1790 std::move(respHandler), "xyz.openbmc_project.ObjectMapper",
1791 "/xyz/openbmc_project/object_mapper",
1792 "xyz.openbmc_project.ObjectMapper", "GetSubTree", path, 0, interfaces);
1793 BMCWEB_LOG_DEBUG << "getInventoryItemsConnections exit";
1794}
1795
1796/**
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001797 * @brief Gets associations from sensors to inventory items.
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001798 *
1799 * Looks for ObjectMapper associations from the specified sensors to related
Anthony Wilsond5005492019-07-31 16:34:17 -05001800 * inventory items. Then finds the associations from those inventory items to
1801 * their LEDs, if any.
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001802 *
1803 * Finds the inventory items asynchronously. Invokes callback when information
1804 * has been obtained.
1805 *
1806 * The callback must have the following signature:
1807 * @code
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001808 * callback(std::shared_ptr<std::vector<InventoryItem>> inventoryItems)
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001809 * @endcode
1810 *
1811 * @param sensorsAsyncResp Pointer to object holding response data.
1812 * @param sensorNames All sensors within the current chassis.
1813 * @param objectMgrPaths Mappings from connection name to DBus object path that
1814 * implements ObjectManager.
1815 * @param callback Callback to invoke when inventory items have been obtained.
1816 */
1817template <typename Callback>
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001818static void getInventoryItemAssociations(
Ed Tanousb5a76932020-09-29 16:16:58 -07001819 const std::shared_ptr<SensorsAsyncResp>& sensorsAsyncResp,
1820 const std::shared_ptr<boost::container::flat_set<std::string>>& sensorNames,
1821 const std::shared_ptr<boost::container::flat_map<std::string, std::string>>&
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001822 objectMgrPaths,
1823 Callback&& callback)
1824{
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001825 BMCWEB_LOG_DEBUG << "getInventoryItemAssociations enter";
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001826
1827 // Response handler for GetManagedObjects
1828 auto respHandler = [callback{std::move(callback)}, sensorsAsyncResp,
1829 sensorNames](const boost::system::error_code ec,
1830 dbus::utility::ManagedObjectType& resp) {
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001831 BMCWEB_LOG_DEBUG << "getInventoryItemAssociations respHandler enter";
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001832 if (ec)
1833 {
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001834 BMCWEB_LOG_ERROR
1835 << "getInventoryItemAssociations respHandler DBus error " << ec;
zhanghch058d1b46d2021-04-01 11:18:24 +08001836 messages::internalError(sensorsAsyncResp->asyncResp->res);
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001837 return;
1838 }
1839
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001840 // Create vector to hold list of inventory items
1841 std::shared_ptr<std::vector<InventoryItem>> inventoryItems =
1842 std::make_shared<std::vector<InventoryItem>>();
1843
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001844 // Loop through returned object paths
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001845 std::string sensorAssocPath;
1846 sensorAssocPath.reserve(128); // avoid memory allocations
1847 for (const auto& objDictEntry : resp)
1848 {
1849 const std::string& objPath =
1850 static_cast<const std::string&>(objDictEntry.first);
1851 const boost::container::flat_map<
1852 std::string, boost::container::flat_map<
1853 std::string, dbus::utility::DbusVariantType>>&
1854 interfacesDict = objDictEntry.second;
1855
1856 // If path is inventory association for one of the specified sensors
1857 for (const std::string& sensorName : *sensorNames)
1858 {
1859 sensorAssocPath = sensorName;
1860 sensorAssocPath += "/inventory";
1861 if (objPath == sensorAssocPath)
1862 {
1863 // Get Association interface for object path
1864 auto assocIt =
1865 interfacesDict.find("xyz.openbmc_project.Association");
1866 if (assocIt != interfacesDict.end())
1867 {
1868 // Get inventory item from end point
1869 auto endpointsIt = assocIt->second.find("endpoints");
1870 if (endpointsIt != assocIt->second.end())
1871 {
1872 const std::vector<std::string>* endpoints =
1873 std::get_if<std::vector<std::string>>(
1874 &endpointsIt->second);
1875 if ((endpoints != nullptr) && !endpoints->empty())
1876 {
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001877 // Add inventory item to vector
1878 const std::string& invItemPath =
1879 endpoints->front();
1880 addInventoryItem(inventoryItems, invItemPath,
1881 sensorName);
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001882 }
1883 }
1884 }
1885 break;
1886 }
1887 }
1888 }
1889
Anthony Wilsond5005492019-07-31 16:34:17 -05001890 // Now loop through the returned object paths again, this time to
1891 // find the leds associated with the inventory items we just found
1892 std::string inventoryAssocPath;
1893 inventoryAssocPath.reserve(128); // avoid memory allocations
1894 for (const auto& objDictEntry : resp)
1895 {
1896 const std::string& objPath =
1897 static_cast<const std::string&>(objDictEntry.first);
1898 const boost::container::flat_map<
1899 std::string, boost::container::flat_map<
1900 std::string, dbus::utility::DbusVariantType>>&
1901 interfacesDict = objDictEntry.second;
1902
1903 for (InventoryItem& inventoryItem : *inventoryItems)
1904 {
1905 inventoryAssocPath = inventoryItem.objectPath;
1906 inventoryAssocPath += "/leds";
1907 if (objPath == inventoryAssocPath)
1908 {
1909 // Get Association interface for object path
1910 auto assocIt =
1911 interfacesDict.find("xyz.openbmc_project.Association");
1912 if (assocIt != interfacesDict.end())
1913 {
1914 // Get inventory item from end point
1915 auto endpointsIt = assocIt->second.find("endpoints");
1916 if (endpointsIt != assocIt->second.end())
1917 {
1918 const std::vector<std::string>* endpoints =
1919 std::get_if<std::vector<std::string>>(
1920 &endpointsIt->second);
1921 if ((endpoints != nullptr) && !endpoints->empty())
1922 {
1923 // Store LED path in inventory item
1924 const std::string& ledPath = endpoints->front();
1925 inventoryItem.ledObjectPath = ledPath;
1926 }
1927 }
1928 }
1929 break;
1930 }
1931 }
1932 }
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001933 callback(inventoryItems);
1934 BMCWEB_LOG_DEBUG << "getInventoryItemAssociations respHandler exit";
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001935 };
1936
1937 // Find DBus object path that implements ObjectManager for ObjectMapper
1938 std::string connection = "xyz.openbmc_project.ObjectMapper";
1939 auto iter = objectMgrPaths->find(connection);
1940 const std::string& objectMgrPath =
1941 (iter != objectMgrPaths->end()) ? iter->second : "/";
1942 BMCWEB_LOG_DEBUG << "ObjectManager path for " << connection << " is "
1943 << objectMgrPath;
1944
1945 // Call GetManagedObjects on the ObjectMapper to get all associations
1946 crow::connections::systemBus->async_method_call(
1947 std::move(respHandler), connection, objectMgrPath,
1948 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
1949
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001950 BMCWEB_LOG_DEBUG << "getInventoryItemAssociations exit";
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001951}
1952
1953/**
Anthony Wilsond5005492019-07-31 16:34:17 -05001954 * @brief Gets D-Bus data for inventory item leds associated with sensors.
1955 *
1956 * Uses the specified connections (services) to obtain D-Bus data for inventory
1957 * item leds associated with sensors. Stores the resulting data in the
1958 * inventoryItems vector.
1959 *
1960 * This data is later used to provide sensor property values in the JSON
1961 * response.
1962 *
1963 * Finds the inventory item led data asynchronously. Invokes callback when data
1964 * has been obtained.
1965 *
1966 * The callback must have the following signature:
1967 * @code
Gunnar Mills42cbe532019-08-15 15:26:54 -05001968 * callback()
Anthony Wilsond5005492019-07-31 16:34:17 -05001969 * @endcode
1970 *
1971 * This function is called recursively, obtaining data asynchronously from one
1972 * connection in each call. This ensures the callback is not invoked until the
1973 * last asynchronous function has completed.
1974 *
1975 * @param sensorsAsyncResp Pointer to object holding response data.
1976 * @param inventoryItems D-Bus inventory items associated with sensors.
1977 * @param ledConnections Connections that provide data for the inventory leds.
1978 * @param callback Callback to invoke when inventory data has been obtained.
1979 * @param ledConnectionsIndex Current index in ledConnections. Only specified
1980 * in recursive calls to this function.
1981 */
1982template <typename Callback>
1983void getInventoryLedData(
1984 std::shared_ptr<SensorsAsyncResp> sensorsAsyncResp,
1985 std::shared_ptr<std::vector<InventoryItem>> inventoryItems,
1986 std::shared_ptr<boost::container::flat_map<std::string, std::string>>
1987 ledConnections,
1988 Callback&& callback, size_t ledConnectionsIndex = 0)
1989{
1990 BMCWEB_LOG_DEBUG << "getInventoryLedData enter";
1991
1992 // If no more connections left, call callback
1993 if (ledConnectionsIndex >= ledConnections->size())
1994 {
Gunnar Mills42cbe532019-08-15 15:26:54 -05001995 callback();
Anthony Wilsond5005492019-07-31 16:34:17 -05001996 BMCWEB_LOG_DEBUG << "getInventoryLedData exit";
1997 return;
1998 }
1999
2000 // Get inventory item data from current connection
2001 auto it = ledConnections->nth(ledConnectionsIndex);
2002 if (it != ledConnections->end())
2003 {
2004 const std::string& ledPath = (*it).first;
2005 const std::string& ledConnection = (*it).second;
2006 // Response handler for Get State property
2007 auto respHandler =
2008 [sensorsAsyncResp, inventoryItems, ledConnections, ledPath,
2009 callback{std::move(callback)},
2010 ledConnectionsIndex](const boost::system::error_code ec,
2011 const std::variant<std::string>& ledState) {
2012 BMCWEB_LOG_DEBUG << "getInventoryLedData respHandler enter";
2013 if (ec)
2014 {
2015 BMCWEB_LOG_ERROR
2016 << "getInventoryLedData respHandler DBus error " << ec;
zhanghch058d1b46d2021-04-01 11:18:24 +08002017 messages::internalError(sensorsAsyncResp->asyncResp->res);
Anthony Wilsond5005492019-07-31 16:34:17 -05002018 return;
2019 }
2020
2021 const std::string* state = std::get_if<std::string>(&ledState);
2022 if (state != nullptr)
2023 {
2024 BMCWEB_LOG_DEBUG << "Led state: " << *state;
2025 // Find inventory item with this LED object path
2026 InventoryItem* inventoryItem =
2027 findInventoryItemForLed(*inventoryItems, ledPath);
2028 if (inventoryItem != nullptr)
2029 {
2030 // Store LED state in InventoryItem
2031 if (boost::ends_with(*state, "On"))
2032 {
2033 inventoryItem->ledState = LedState::ON;
2034 }
2035 else if (boost::ends_with(*state, "Blink"))
2036 {
2037 inventoryItem->ledState = LedState::BLINK;
2038 }
2039 else if (boost::ends_with(*state, "Off"))
2040 {
2041 inventoryItem->ledState = LedState::OFF;
2042 }
2043 else
2044 {
2045 inventoryItem->ledState = LedState::UNKNOWN;
2046 }
2047 }
2048 }
2049 else
2050 {
2051 BMCWEB_LOG_DEBUG << "Failed to find State data for LED: "
2052 << ledPath;
2053 }
2054
2055 // Recurse to get LED data from next connection
2056 getInventoryLedData(sensorsAsyncResp, inventoryItems,
2057 ledConnections, std::move(callback),
2058 ledConnectionsIndex + 1);
2059
2060 BMCWEB_LOG_DEBUG << "getInventoryLedData respHandler exit";
2061 };
2062
2063 // Get the State property for the current LED
2064 crow::connections::systemBus->async_method_call(
2065 std::move(respHandler), ledConnection, ledPath,
2066 "org.freedesktop.DBus.Properties", "Get",
2067 "xyz.openbmc_project.Led.Physical", "State");
2068 }
2069
2070 BMCWEB_LOG_DEBUG << "getInventoryLedData exit";
2071}
2072
2073/**
2074 * @brief Gets LED data for LEDs associated with given inventory items.
2075 *
2076 * Gets the D-Bus connections (services) that provide LED data for the LEDs
2077 * associated with the specified inventory items. Then gets the LED data from
2078 * each connection and stores it in the inventory item.
2079 *
2080 * This data is later used to provide sensor property values in the JSON
2081 * response.
2082 *
2083 * Finds the LED data asynchronously. Invokes callback when information has
2084 * been obtained.
2085 *
2086 * The callback must have the following signature:
2087 * @code
Gunnar Mills42cbe532019-08-15 15:26:54 -05002088 * callback()
Anthony Wilsond5005492019-07-31 16:34:17 -05002089 * @endcode
2090 *
2091 * @param sensorsAsyncResp Pointer to object holding response data.
2092 * @param inventoryItems D-Bus inventory items associated with sensors.
2093 * @param callback Callback to invoke when inventory items have been obtained.
2094 */
2095template <typename Callback>
2096void getInventoryLeds(
2097 std::shared_ptr<SensorsAsyncResp> sensorsAsyncResp,
2098 std::shared_ptr<std::vector<InventoryItem>> inventoryItems,
2099 Callback&& callback)
2100{
2101 BMCWEB_LOG_DEBUG << "getInventoryLeds enter";
2102
2103 const std::string path = "/xyz/openbmc_project";
2104 const std::array<std::string, 1> interfaces = {
2105 "xyz.openbmc_project.Led.Physical"};
2106
2107 // Response handler for parsing output from GetSubTree
2108 auto respHandler = [callback{std::move(callback)}, sensorsAsyncResp,
2109 inventoryItems](const boost::system::error_code ec,
2110 const GetSubTreeType& subtree) {
2111 BMCWEB_LOG_DEBUG << "getInventoryLeds respHandler enter";
2112 if (ec)
2113 {
zhanghch058d1b46d2021-04-01 11:18:24 +08002114 messages::internalError(sensorsAsyncResp->asyncResp->res);
Anthony Wilsond5005492019-07-31 16:34:17 -05002115 BMCWEB_LOG_ERROR << "getInventoryLeds respHandler DBus error "
2116 << ec;
2117 return;
2118 }
2119
2120 // Build map of LED object paths to connections
2121 std::shared_ptr<boost::container::flat_map<std::string, std::string>>
2122 ledConnections = std::make_shared<
2123 boost::container::flat_map<std::string, std::string>>();
2124
2125 // Loop through objects from GetSubTree
2126 for (const std::pair<
2127 std::string,
2128 std::vector<std::pair<std::string, std::vector<std::string>>>>&
2129 object : subtree)
2130 {
2131 // Check if object path is LED for one of the specified inventory
2132 // items
2133 const std::string& ledPath = object.first;
2134 if (findInventoryItemForLed(*inventoryItems, ledPath) != nullptr)
2135 {
2136 // Add mapping from ledPath to connection
2137 const std::string& connection = object.second.begin()->first;
2138 (*ledConnections)[ledPath] = connection;
2139 BMCWEB_LOG_DEBUG << "Added mapping " << ledPath << " -> "
2140 << connection;
2141 }
2142 }
2143
2144 getInventoryLedData(sensorsAsyncResp, inventoryItems, ledConnections,
2145 std::move(callback));
2146 BMCWEB_LOG_DEBUG << "getInventoryLeds respHandler exit";
2147 };
2148 // Make call to ObjectMapper to find all inventory items
2149 crow::connections::systemBus->async_method_call(
2150 std::move(respHandler), "xyz.openbmc_project.ObjectMapper",
2151 "/xyz/openbmc_project/object_mapper",
2152 "xyz.openbmc_project.ObjectMapper", "GetSubTree", path, 0, interfaces);
2153 BMCWEB_LOG_DEBUG << "getInventoryLeds exit";
2154}
2155
2156/**
Gunnar Mills42cbe532019-08-15 15:26:54 -05002157 * @brief Gets D-Bus data for Power Supply Attributes such as EfficiencyPercent
2158 *
2159 * Uses the specified connections (services) (currently assumes just one) to
2160 * obtain D-Bus data for Power Supply Attributes. Stores the resulting data in
2161 * the inventoryItems vector. Only stores data in Power Supply inventoryItems.
2162 *
2163 * This data is later used to provide sensor property values in the JSON
2164 * response.
2165 *
2166 * Finds the Power Supply Attributes data asynchronously. Invokes callback
2167 * when data has been obtained.
2168 *
2169 * The callback must have the following signature:
2170 * @code
2171 * callback(std::shared_ptr<std::vector<InventoryItem>> inventoryItems)
2172 * @endcode
2173 *
2174 * @param sensorsAsyncResp Pointer to object holding response data.
2175 * @param inventoryItems D-Bus inventory items associated with sensors.
2176 * @param psAttributesConnections Connections that provide data for the Power
2177 * Supply Attributes
2178 * @param callback Callback to invoke when data has been obtained.
2179 */
2180template <typename Callback>
2181void getPowerSupplyAttributesData(
Ed Tanousb5a76932020-09-29 16:16:58 -07002182 const std::shared_ptr<SensorsAsyncResp>& sensorsAsyncResp,
Gunnar Mills42cbe532019-08-15 15:26:54 -05002183 std::shared_ptr<std::vector<InventoryItem>> inventoryItems,
2184 const boost::container::flat_map<std::string, std::string>&
2185 psAttributesConnections,
2186 Callback&& callback)
2187{
2188 BMCWEB_LOG_DEBUG << "getPowerSupplyAttributesData enter";
2189
2190 if (psAttributesConnections.empty())
2191 {
2192 BMCWEB_LOG_DEBUG << "Can't find PowerSupplyAttributes, no connections!";
2193 callback(inventoryItems);
2194 return;
2195 }
2196
2197 // Assuming just one connection (service) for now
2198 auto it = psAttributesConnections.nth(0);
2199
2200 const std::string& psAttributesPath = (*it).first;
2201 const std::string& psAttributesConnection = (*it).second;
2202
2203 // Response handler for Get DeratingFactor property
2204 auto respHandler = [sensorsAsyncResp, inventoryItems,
2205 callback{std::move(callback)}](
2206 const boost::system::error_code ec,
2207 const std::variant<uint32_t>& deratingFactor) {
2208 BMCWEB_LOG_DEBUG << "getPowerSupplyAttributesData respHandler enter";
2209 if (ec)
2210 {
2211 BMCWEB_LOG_ERROR
2212 << "getPowerSupplyAttributesData respHandler DBus error " << ec;
zhanghch058d1b46d2021-04-01 11:18:24 +08002213 messages::internalError(sensorsAsyncResp->asyncResp->res);
Gunnar Mills42cbe532019-08-15 15:26:54 -05002214 return;
2215 }
2216
2217 const uint32_t* value = std::get_if<uint32_t>(&deratingFactor);
2218 if (value != nullptr)
2219 {
2220 BMCWEB_LOG_DEBUG << "PS EfficiencyPercent value: " << *value;
2221 // Store value in Power Supply Inventory Items
2222 for (InventoryItem& inventoryItem : *inventoryItems)
2223 {
2224 if (inventoryItem.isPowerSupply == true)
2225 {
2226 inventoryItem.powerSupplyEfficiencyPercent =
2227 static_cast<int>(*value);
2228 }
2229 }
2230 }
2231 else
2232 {
2233 BMCWEB_LOG_DEBUG
2234 << "Failed to find EfficiencyPercent value for PowerSupplies";
2235 }
2236
2237 BMCWEB_LOG_DEBUG << "getPowerSupplyAttributesData respHandler exit";
2238 callback(inventoryItems);
2239 };
2240
2241 // Get the DeratingFactor property for the PowerSupplyAttributes
2242 // Currently only property on the interface/only one we care about
2243 crow::connections::systemBus->async_method_call(
2244 std::move(respHandler), psAttributesConnection, psAttributesPath,
2245 "org.freedesktop.DBus.Properties", "Get",
2246 "xyz.openbmc_project.Control.PowerSupplyAttributes", "DeratingFactor");
2247
2248 BMCWEB_LOG_DEBUG << "getPowerSupplyAttributesData exit";
2249}
2250
2251/**
2252 * @brief Gets the Power Supply Attributes such as EfficiencyPercent
2253 *
2254 * Gets the D-Bus connection (service) that provides Power Supply Attributes
2255 * data. Then gets the Power Supply Attributes data from the connection
2256 * (currently just assumes 1 connection) and stores the data in the inventory
2257 * item.
2258 *
2259 * This data is later used to provide sensor property values in the JSON
2260 * response. DeratingFactor on D-Bus is mapped to EfficiencyPercent on Redfish.
2261 *
2262 * Finds the Power Supply Attributes data asynchronously. Invokes callback
2263 * when information has been obtained.
2264 *
2265 * The callback must have the following signature:
2266 * @code
2267 * callback(std::shared_ptr<std::vector<InventoryItem>> inventoryItems)
2268 * @endcode
2269 *
2270 * @param sensorsAsyncResp Pointer to object holding response data.
2271 * @param inventoryItems D-Bus inventory items associated with sensors.
2272 * @param callback Callback to invoke when data has been obtained.
2273 */
2274template <typename Callback>
2275void getPowerSupplyAttributes(
2276 std::shared_ptr<SensorsAsyncResp> sensorsAsyncResp,
2277 std::shared_ptr<std::vector<InventoryItem>> inventoryItems,
2278 Callback&& callback)
2279{
2280 BMCWEB_LOG_DEBUG << "getPowerSupplyAttributes enter";
2281
2282 // Only need the power supply attributes when the Power Schema
Adrian Ambrożewicza0ec28b2020-04-10 14:47:28 +02002283 if (sensorsAsyncResp->chassisSubNode != sensors::node::power)
Gunnar Mills42cbe532019-08-15 15:26:54 -05002284 {
2285 BMCWEB_LOG_DEBUG << "getPowerSupplyAttributes exit since not Power";
2286 callback(inventoryItems);
2287 return;
2288 }
2289
2290 const std::array<std::string, 1> interfaces = {
2291 "xyz.openbmc_project.Control.PowerSupplyAttributes"};
2292
2293 // Response handler for parsing output from GetSubTree
2294 auto respHandler = [callback{std::move(callback)}, sensorsAsyncResp,
2295 inventoryItems](const boost::system::error_code ec,
2296 const GetSubTreeType& subtree) {
2297 BMCWEB_LOG_DEBUG << "getPowerSupplyAttributes respHandler enter";
2298 if (ec)
2299 {
zhanghch058d1b46d2021-04-01 11:18:24 +08002300 messages::internalError(sensorsAsyncResp->asyncResp->res);
Gunnar Mills42cbe532019-08-15 15:26:54 -05002301 BMCWEB_LOG_ERROR
2302 << "getPowerSupplyAttributes respHandler DBus error " << ec;
2303 return;
2304 }
2305 if (subtree.size() == 0)
2306 {
2307 BMCWEB_LOG_DEBUG << "Can't find Power Supply Attributes!";
2308 callback(inventoryItems);
2309 return;
2310 }
2311
2312 // Currently we only support 1 power supply attribute, use this for
2313 // all the power supplies. Build map of object path to connection.
2314 // Assume just 1 connection and 1 path for now.
2315 boost::container::flat_map<std::string, std::string>
2316 psAttributesConnections;
2317
2318 if (subtree[0].first.empty() || subtree[0].second.empty())
2319 {
2320 BMCWEB_LOG_DEBUG << "Power Supply Attributes mapper error!";
2321 callback(inventoryItems);
2322 return;
2323 }
2324
2325 const std::string& psAttributesPath = subtree[0].first;
2326 const std::string& connection = subtree[0].second.begin()->first;
2327
2328 if (connection.empty())
2329 {
2330 BMCWEB_LOG_DEBUG << "Power Supply Attributes mapper error!";
2331 callback(inventoryItems);
2332 return;
2333 }
2334
2335 psAttributesConnections[psAttributesPath] = connection;
2336 BMCWEB_LOG_DEBUG << "Added mapping " << psAttributesPath << " -> "
2337 << connection;
2338
2339 getPowerSupplyAttributesData(sensorsAsyncResp, inventoryItems,
2340 psAttributesConnections,
2341 std::move(callback));
2342 BMCWEB_LOG_DEBUG << "getPowerSupplyAttributes respHandler exit";
2343 };
2344 // Make call to ObjectMapper to find the PowerSupplyAttributes service
2345 crow::connections::systemBus->async_method_call(
2346 std::move(respHandler), "xyz.openbmc_project.ObjectMapper",
2347 "/xyz/openbmc_project/object_mapper",
2348 "xyz.openbmc_project.ObjectMapper", "GetSubTree",
2349 "/xyz/openbmc_project", 0, interfaces);
2350 BMCWEB_LOG_DEBUG << "getPowerSupplyAttributes exit";
2351}
2352
2353/**
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05002354 * @brief Gets inventory items associated with sensors.
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05002355 *
2356 * Finds the inventory items that are associated with the specified sensors.
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05002357 * Then gets D-Bus data for the inventory items, such as presence and VPD.
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05002358 *
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05002359 * This data is later used to provide sensor property values in the JSON
2360 * response.
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05002361 *
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05002362 * Finds the inventory items asynchronously. Invokes callback when the
2363 * inventory items have been obtained.
2364 *
2365 * The callback must have the following signature:
2366 * @code
2367 * callback(std::shared_ptr<std::vector<InventoryItem>> inventoryItems)
2368 * @endcode
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05002369 *
2370 * @param sensorsAsyncResp Pointer to object holding response data.
2371 * @param sensorNames All sensors within the current chassis.
2372 * @param objectMgrPaths Mappings from connection name to DBus object path that
2373 * implements ObjectManager.
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05002374 * @param callback Callback to invoke when inventory items have been obtained.
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05002375 */
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05002376template <typename Callback>
2377static void getInventoryItems(
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05002378 std::shared_ptr<SensorsAsyncResp> sensorsAsyncResp,
2379 const std::shared_ptr<boost::container::flat_set<std::string>> sensorNames,
2380 std::shared_ptr<boost::container::flat_map<std::string, std::string>>
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05002381 objectMgrPaths,
2382 Callback&& callback)
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05002383{
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05002384 BMCWEB_LOG_DEBUG << "getInventoryItems enter";
2385 auto getInventoryItemAssociationsCb =
2386 [sensorsAsyncResp, objectMgrPaths, callback{std::move(callback)}](
2387 std::shared_ptr<std::vector<InventoryItem>> inventoryItems) {
2388 BMCWEB_LOG_DEBUG << "getInventoryItemAssociationsCb enter";
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05002389 auto getInventoryItemsConnectionsCb =
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05002390 [sensorsAsyncResp, inventoryItems, objectMgrPaths,
2391 callback{std::move(callback)}](
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05002392 std::shared_ptr<boost::container::flat_set<std::string>>
2393 invConnections) {
2394 BMCWEB_LOG_DEBUG << "getInventoryItemsConnectionsCb enter";
Anthony Wilsond5005492019-07-31 16:34:17 -05002395 auto getInventoryItemsDataCb =
2396 [sensorsAsyncResp, inventoryItems,
2397 callback{std::move(callback)}]() {
2398 BMCWEB_LOG_DEBUG << "getInventoryItemsDataCb enter";
Gunnar Mills42cbe532019-08-15 15:26:54 -05002399
2400 auto getInventoryLedsCb = [sensorsAsyncResp,
2401 inventoryItems,
2402 callback{std::move(
2403 callback)}]() {
2404 BMCWEB_LOG_DEBUG << "getInventoryLedsCb enter";
2405 // Find Power Supply Attributes and get the data
2406 getPowerSupplyAttributes(sensorsAsyncResp,
2407 inventoryItems,
2408 std::move(callback));
2409 BMCWEB_LOG_DEBUG << "getInventoryLedsCb exit";
2410 };
2411
Anthony Wilsond5005492019-07-31 16:34:17 -05002412 // Find led connections and get the data
2413 getInventoryLeds(sensorsAsyncResp, inventoryItems,
Gunnar Mills42cbe532019-08-15 15:26:54 -05002414 std::move(getInventoryLedsCb));
Anthony Wilsond5005492019-07-31 16:34:17 -05002415 BMCWEB_LOG_DEBUG << "getInventoryItemsDataCb exit";
2416 };
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05002417
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05002418 // Get inventory item data from connections
2419 getInventoryItemsData(sensorsAsyncResp, inventoryItems,
2420 invConnections, objectMgrPaths,
Anthony Wilsond5005492019-07-31 16:34:17 -05002421 std::move(getInventoryItemsDataCb));
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05002422 BMCWEB_LOG_DEBUG << "getInventoryItemsConnectionsCb exit";
2423 };
2424
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05002425 // Get connections that provide inventory item data
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05002426 getInventoryItemsConnections(
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05002427 sensorsAsyncResp, inventoryItems,
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05002428 std::move(getInventoryItemsConnectionsCb));
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05002429 BMCWEB_LOG_DEBUG << "getInventoryItemAssociationsCb exit";
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05002430 };
2431
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05002432 // Get associations from sensors to inventory items
2433 getInventoryItemAssociations(sensorsAsyncResp, sensorNames, objectMgrPaths,
2434 std::move(getInventoryItemAssociationsCb));
2435 BMCWEB_LOG_DEBUG << "getInventoryItems exit";
2436}
2437
2438/**
2439 * @brief Returns JSON PowerSupply object for the specified inventory item.
2440 *
2441 * Searches for a JSON PowerSupply object that matches the specified inventory
2442 * item. If one is not found, a new PowerSupply object is added to the JSON
2443 * array.
2444 *
2445 * Multiple sensors are often associated with one power supply inventory item.
2446 * As a result, multiple sensor values are stored in one JSON PowerSupply
2447 * object.
2448 *
2449 * @param powerSupplyArray JSON array containing Redfish PowerSupply objects.
2450 * @param inventoryItem Inventory item for the power supply.
2451 * @param chassisId Chassis that contains the power supply.
2452 * @return JSON PowerSupply object for the specified inventory item.
2453 */
Ed Tanous23a21a12020-07-25 04:45:05 +00002454inline nlohmann::json& getPowerSupply(nlohmann::json& powerSupplyArray,
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05002455 const InventoryItem& inventoryItem,
2456 const std::string& chassisId)
2457{
2458 // Check if matching PowerSupply object already exists in JSON array
2459 for (nlohmann::json& powerSupply : powerSupplyArray)
2460 {
2461 if (powerSupply["MemberId"] == inventoryItem.name)
2462 {
2463 return powerSupply;
2464 }
2465 }
2466
2467 // Add new PowerSupply object to JSON array
2468 powerSupplyArray.push_back({});
2469 nlohmann::json& powerSupply = powerSupplyArray.back();
2470 powerSupply["@odata.id"] =
2471 "/redfish/v1/Chassis/" + chassisId + "/Power#/PowerSupplies/";
2472 powerSupply["MemberId"] = inventoryItem.name;
2473 powerSupply["Name"] = boost::replace_all_copy(inventoryItem.name, "_", " ");
2474 powerSupply["Manufacturer"] = inventoryItem.manufacturer;
2475 powerSupply["Model"] = inventoryItem.model;
2476 powerSupply["PartNumber"] = inventoryItem.partNumber;
2477 powerSupply["SerialNumber"] = inventoryItem.serialNumber;
Anthony Wilsond5005492019-07-31 16:34:17 -05002478 setLedState(powerSupply, &inventoryItem);
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05002479
Gunnar Mills42cbe532019-08-15 15:26:54 -05002480 if (inventoryItem.powerSupplyEfficiencyPercent >= 0)
2481 {
2482 powerSupply["EfficiencyPercent"] =
2483 inventoryItem.powerSupplyEfficiencyPercent;
2484 }
2485
2486 powerSupply["Status"]["State"] = getState(&inventoryItem);
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05002487 const char* health = inventoryItem.isFunctional ? "OK" : "Critical";
2488 powerSupply["Status"]["Health"] = health;
2489
2490 return powerSupply;
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05002491}
2492
2493/**
Shawn McCarneyde629b62019-03-08 10:42:51 -06002494 * @brief Gets the values of the specified sensors.
2495 *
2496 * Stores the results as JSON in the SensorsAsyncResp.
2497 *
2498 * Gets the sensor values asynchronously. Stores the results later when the
2499 * information has been obtained.
2500 *
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05002501 * The sensorNames set contains all requested sensors for the current chassis.
Shawn McCarneyde629b62019-03-08 10:42:51 -06002502 *
2503 * To minimize the number of DBus calls, the DBus method
2504 * org.freedesktop.DBus.ObjectManager.GetManagedObjects() is used to get the
2505 * values of all sensors provided by a connection (service).
2506 *
2507 * The connections set contains all the connections that provide sensor values.
2508 *
2509 * The objectMgrPaths map contains mappings from a connection name to the
2510 * corresponding DBus object path that implements ObjectManager.
2511 *
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05002512 * The InventoryItem vector contains D-Bus inventory items associated with the
2513 * sensors. Inventory item data is needed for some Redfish sensor properties.
2514 *
Shawn McCarneyde629b62019-03-08 10:42:51 -06002515 * @param SensorsAsyncResp Pointer to object holding response data.
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05002516 * @param sensorNames All requested sensors within the current chassis.
Shawn McCarneyde629b62019-03-08 10:42:51 -06002517 * @param connections Connections that provide sensor values.
2518 * @param objectMgrPaths Mappings from connection name to DBus object path that
2519 * implements ObjectManager.
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05002520 * @param inventoryItems Inventory items associated with the sensors.
Shawn McCarneyde629b62019-03-08 10:42:51 -06002521 */
Ed Tanous23a21a12020-07-25 04:45:05 +00002522inline void getSensorData(
Ed Tanous81ce6092020-12-17 16:54:55 +00002523 const std::shared_ptr<SensorsAsyncResp>& sensorsAsyncResp,
Ed Tanousb5a76932020-09-29 16:16:58 -07002524 const std::shared_ptr<boost::container::flat_set<std::string>>& sensorNames,
Shawn McCarneyde629b62019-03-08 10:42:51 -06002525 const boost::container::flat_set<std::string>& connections,
Ed Tanousb5a76932020-09-29 16:16:58 -07002526 const std::shared_ptr<boost::container::flat_map<std::string, std::string>>&
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05002527 objectMgrPaths,
Ed Tanousb5a76932020-09-29 16:16:58 -07002528 const std::shared_ptr<std::vector<InventoryItem>>& inventoryItems)
Shawn McCarneyde629b62019-03-08 10:42:51 -06002529{
2530 BMCWEB_LOG_DEBUG << "getSensorData enter";
2531 // Get managed objects from all services exposing sensors
2532 for (const std::string& connection : connections)
2533 {
2534 // Response handler to process managed objects
Ed Tanous81ce6092020-12-17 16:54:55 +00002535 auto getManagedObjectsCb = [sensorsAsyncResp, sensorNames,
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05002536 inventoryItems](
Shawn McCarneyde629b62019-03-08 10:42:51 -06002537 const boost::system::error_code ec,
2538 ManagedObjectsVectorType& resp) {
2539 BMCWEB_LOG_DEBUG << "getManagedObjectsCb enter";
2540 if (ec)
2541 {
2542 BMCWEB_LOG_ERROR << "getManagedObjectsCb DBUS error: " << ec;
zhanghch058d1b46d2021-04-01 11:18:24 +08002543 messages::internalError(sensorsAsyncResp->asyncResp->res);
Shawn McCarneyde629b62019-03-08 10:42:51 -06002544 return;
2545 }
2546 // Go through all objects and update response with sensor data
2547 for (const auto& objDictEntry : resp)
2548 {
2549 const std::string& objPath =
2550 static_cast<const std::string&>(objDictEntry.first);
2551 BMCWEB_LOG_DEBUG << "getManagedObjectsCb parsing object "
2552 << objPath;
2553
Shawn McCarneyde629b62019-03-08 10:42:51 -06002554 std::vector<std::string> split;
2555 // Reserve space for
2556 // /xyz/openbmc_project/sensors/<name>/<subname>
2557 split.reserve(6);
2558 boost::algorithm::split(split, objPath, boost::is_any_of("/"));
2559 if (split.size() < 6)
2560 {
2561 BMCWEB_LOG_ERROR << "Got path that isn't long enough "
2562 << objPath;
2563 continue;
2564 }
2565 // These indexes aren't intuitive, as boost::split puts an empty
2566 // string at the beginning
2567 const std::string& sensorType = split[4];
2568 const std::string& sensorName = split[5];
2569 BMCWEB_LOG_DEBUG << "sensorName " << sensorName
2570 << " sensorType " << sensorType;
Johnathan Mantey49c53ac2019-05-02 09:22:38 -07002571 if (sensorNames->find(objPath) == sensorNames->end())
Shawn McCarneyde629b62019-03-08 10:42:51 -06002572 {
2573 BMCWEB_LOG_ERROR << sensorName << " not in sensor list ";
2574 continue;
2575 }
2576
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05002577 // Find inventory item (if any) associated with sensor
2578 InventoryItem* inventoryItem =
2579 findInventoryItemForSensor(inventoryItems, objPath);
2580
Anthony Wilson95a3eca2019-06-11 10:44:47 -05002581 const std::string& sensorSchema =
Ed Tanous81ce6092020-12-17 16:54:55 +00002582 sensorsAsyncResp->chassisSubNode;
Anthony Wilson95a3eca2019-06-11 10:44:47 -05002583
2584 nlohmann::json* sensorJson = nullptr;
2585
Adrian Ambrożewicza0ec28b2020-04-10 14:47:28 +02002586 if (sensorSchema == sensors::node::sensors)
Shawn McCarneyde629b62019-03-08 10:42:51 -06002587 {
zhanghch058d1b46d2021-04-01 11:18:24 +08002588 sensorsAsyncResp->asyncResp->res.jsonValue["@odata.id"] =
Ed Tanous81ce6092020-12-17 16:54:55 +00002589 "/redfish/v1/Chassis/" + sensorsAsyncResp->chassisId +
2590 "/" + sensorsAsyncResp->chassisSubNode + "/" +
Anthony Wilson95a3eca2019-06-11 10:44:47 -05002591 sensorName;
zhanghch058d1b46d2021-04-01 11:18:24 +08002592 sensorJson = &(sensorsAsyncResp->asyncResp->res.jsonValue);
Shawn McCarneyde629b62019-03-08 10:42:51 -06002593 }
2594 else
2595 {
Ed Tanous271584a2019-07-09 16:24:22 -07002596 std::string fieldName;
Anthony Wilson95a3eca2019-06-11 10:44:47 -05002597 if (sensorType == "temperature")
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05002598 {
Anthony Wilson95a3eca2019-06-11 10:44:47 -05002599 fieldName = "Temperatures";
2600 }
2601 else if (sensorType == "fan" || sensorType == "fan_tach" ||
2602 sensorType == "fan_pwm")
2603 {
2604 fieldName = "Fans";
2605 }
2606 else if (sensorType == "voltage")
2607 {
2608 fieldName = "Voltages";
2609 }
2610 else if (sensorType == "power")
2611 {
2612 if (!sensorName.compare("total_power"))
2613 {
2614 fieldName = "PowerControl";
2615 }
2616 else if ((inventoryItem != nullptr) &&
2617 (inventoryItem->isPowerSupply))
2618 {
2619 fieldName = "PowerSupplies";
2620 }
2621 else
2622 {
2623 // Other power sensors are in SensorCollection
2624 continue;
2625 }
2626 }
2627 else
2628 {
2629 BMCWEB_LOG_ERROR << "Unsure how to handle sensorType "
2630 << sensorType;
2631 continue;
2632 }
2633
2634 nlohmann::json& tempArray =
zhanghch058d1b46d2021-04-01 11:18:24 +08002635 sensorsAsyncResp->asyncResp->res.jsonValue[fieldName];
Anthony Wilson95a3eca2019-06-11 10:44:47 -05002636 if (fieldName == "PowerControl")
2637 {
2638 if (tempArray.empty())
2639 {
2640 // Put multiple "sensors" into a single
2641 // PowerControl. Follows MemberId naming and
2642 // naming in power.hpp.
2643 tempArray.push_back(
2644 {{"@odata.id",
2645 "/redfish/v1/Chassis/" +
Ed Tanous81ce6092020-12-17 16:54:55 +00002646 sensorsAsyncResp->chassisId + "/" +
2647 sensorsAsyncResp->chassisSubNode + "#/" +
Anthony Wilson95a3eca2019-06-11 10:44:47 -05002648 fieldName + "/0"}});
2649 }
2650 sensorJson = &(tempArray.back());
2651 }
2652 else if (fieldName == "PowerSupplies")
2653 {
2654 if (inventoryItem != nullptr)
2655 {
2656 sensorJson =
2657 &(getPowerSupply(tempArray, *inventoryItem,
Ed Tanous81ce6092020-12-17 16:54:55 +00002658 sensorsAsyncResp->chassisId));
Anthony Wilson95a3eca2019-06-11 10:44:47 -05002659 }
2660 }
2661 else
2662 {
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05002663 tempArray.push_back(
2664 {{"@odata.id",
2665 "/redfish/v1/Chassis/" +
Ed Tanous81ce6092020-12-17 16:54:55 +00002666 sensorsAsyncResp->chassisId + "/" +
2667 sensorsAsyncResp->chassisSubNode + "#/" +
Anthony Wilson95a3eca2019-06-11 10:44:47 -05002668 fieldName + "/"}});
2669 sensorJson = &(tempArray.back());
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05002670 }
Johnathan Mantey49c53ac2019-05-02 09:22:38 -07002671 }
Shawn McCarneyde629b62019-03-08 10:42:51 -06002672
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05002673 if (sensorJson != nullptr)
2674 {
Adrian Ambrożewicza0ec28b2020-04-10 14:47:28 +02002675 objectInterfacesToJson(
Ed Tanous81ce6092020-12-17 16:54:55 +00002676 sensorName, sensorType, sensorsAsyncResp,
Adrian Ambrożewicza0ec28b2020-04-10 14:47:28 +02002677 objDictEntry.second, *sensorJson, inventoryItem);
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05002678 }
Shawn McCarneyde629b62019-03-08 10:42:51 -06002679 }
Ed Tanous81ce6092020-12-17 16:54:55 +00002680 if (sensorsAsyncResp.use_count() == 1)
James Feist8bd25cc2019-03-15 15:14:00 -07002681 {
Ed Tanous81ce6092020-12-17 16:54:55 +00002682 sortJSONResponse(sensorsAsyncResp);
2683 if (sensorsAsyncResp->chassisSubNode == sensors::node::thermal)
Johnathan Mantey49c53ac2019-05-02 09:22:38 -07002684 {
Ed Tanous81ce6092020-12-17 16:54:55 +00002685 populateFanRedundancy(sensorsAsyncResp);
Johnathan Mantey49c53ac2019-05-02 09:22:38 -07002686 }
James Feist8bd25cc2019-03-15 15:14:00 -07002687 }
Shawn McCarneyde629b62019-03-08 10:42:51 -06002688 BMCWEB_LOG_DEBUG << "getManagedObjectsCb exit";
2689 };
2690
2691 // Find DBus object path that implements ObjectManager for the current
2692 // connection. If no mapping found, default to "/".
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05002693 auto iter = objectMgrPaths->find(connection);
Shawn McCarneyde629b62019-03-08 10:42:51 -06002694 const std::string& objectMgrPath =
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05002695 (iter != objectMgrPaths->end()) ? iter->second : "/";
Shawn McCarneyde629b62019-03-08 10:42:51 -06002696 BMCWEB_LOG_DEBUG << "ObjectManager path for " << connection << " is "
2697 << objectMgrPath;
2698
2699 crow::connections::systemBus->async_method_call(
2700 getManagedObjectsCb, connection, objectMgrPath,
2701 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
Ed Tanous23a21a12020-07-25 04:45:05 +00002702 }
Shawn McCarneyde629b62019-03-08 10:42:51 -06002703 BMCWEB_LOG_DEBUG << "getSensorData exit";
2704}
2705
Ed Tanous23a21a12020-07-25 04:45:05 +00002706inline void processSensorList(
Ed Tanous81ce6092020-12-17 16:54:55 +00002707 const std::shared_ptr<SensorsAsyncResp>& sensorsAsyncResp,
Ed Tanousb5a76932020-09-29 16:16:58 -07002708 const std::shared_ptr<boost::container::flat_set<std::string>>& sensorNames)
Anthony Wilson95a3eca2019-06-11 10:44:47 -05002709{
2710 auto getConnectionCb =
Ed Tanous81ce6092020-12-17 16:54:55 +00002711 [sensorsAsyncResp, sensorNames](
Anthony Wilson95a3eca2019-06-11 10:44:47 -05002712 const boost::container::flat_set<std::string>& connections) {
2713 BMCWEB_LOG_DEBUG << "getConnectionCb enter";
2714 auto getObjectManagerPathsCb =
Ed Tanous81ce6092020-12-17 16:54:55 +00002715 [sensorsAsyncResp, sensorNames,
Ed Tanousb5a76932020-09-29 16:16:58 -07002716 connections](const std::shared_ptr<boost::container::flat_map<
2717 std::string, std::string>>& objectMgrPaths) {
Anthony Wilson95a3eca2019-06-11 10:44:47 -05002718 BMCWEB_LOG_DEBUG << "getObjectManagerPathsCb enter";
2719 auto getInventoryItemsCb =
Ed Tanous81ce6092020-12-17 16:54:55 +00002720 [sensorsAsyncResp, sensorNames, connections,
Anthony Wilson95a3eca2019-06-11 10:44:47 -05002721 objectMgrPaths](
Ed Tanousf23b7292020-10-15 09:41:17 -07002722 const std::shared_ptr<std::vector<InventoryItem>>&
Anthony Wilson95a3eca2019-06-11 10:44:47 -05002723 inventoryItems) {
2724 BMCWEB_LOG_DEBUG << "getInventoryItemsCb enter";
2725 // Get sensor data and store results in JSON
Ed Tanous81ce6092020-12-17 16:54:55 +00002726 getSensorData(sensorsAsyncResp, sensorNames,
Anthony Wilson95a3eca2019-06-11 10:44:47 -05002727 connections, objectMgrPaths,
Ed Tanousf23b7292020-10-15 09:41:17 -07002728 inventoryItems);
Anthony Wilson95a3eca2019-06-11 10:44:47 -05002729 BMCWEB_LOG_DEBUG << "getInventoryItemsCb exit";
2730 };
2731
2732 // Get inventory items associated with sensors
Ed Tanous81ce6092020-12-17 16:54:55 +00002733 getInventoryItems(sensorsAsyncResp, sensorNames,
Anthony Wilson95a3eca2019-06-11 10:44:47 -05002734 objectMgrPaths,
2735 std::move(getInventoryItemsCb));
2736
2737 BMCWEB_LOG_DEBUG << "getObjectManagerPathsCb exit";
2738 };
2739
2740 // Get mapping from connection names to the DBus object
2741 // paths that implement the ObjectManager interface
Ed Tanous81ce6092020-12-17 16:54:55 +00002742 getObjectManagerPaths(sensorsAsyncResp,
Anthony Wilson95a3eca2019-06-11 10:44:47 -05002743 std::move(getObjectManagerPathsCb));
2744 BMCWEB_LOG_DEBUG << "getConnectionCb exit";
2745 };
2746
2747 // Get set of connections that provide sensor values
Ed Tanous81ce6092020-12-17 16:54:55 +00002748 getConnections(sensorsAsyncResp, sensorNames, std::move(getConnectionCb));
Anthony Wilson95a3eca2019-06-11 10:44:47 -05002749}
2750
Shawn McCarneyde629b62019-03-08 10:42:51 -06002751/**
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +01002752 * @brief Entry point for retrieving sensors data related to requested
2753 * chassis.
Kowalski, Kamil588c3f02018-04-03 14:55:27 +02002754 * @param SensorsAsyncResp Pointer to object holding response data
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +01002755 */
Ed Tanousb5a76932020-09-29 16:16:58 -07002756inline void
Ed Tanous81ce6092020-12-17 16:54:55 +00002757 getChassisData(const std::shared_ptr<SensorsAsyncResp>& sensorsAsyncResp)
Ed Tanous1abe55e2018-09-05 08:30:59 -07002758{
2759 BMCWEB_LOG_DEBUG << "getChassisData enter";
Johnathan Mantey49c53ac2019-05-02 09:22:38 -07002760 auto getChassisCb =
Ed Tanous81ce6092020-12-17 16:54:55 +00002761 [sensorsAsyncResp](
Ed Tanousf23b7292020-10-15 09:41:17 -07002762 const std::shared_ptr<boost::container::flat_set<std::string>>&
Johnathan Mantey49c53ac2019-05-02 09:22:38 -07002763 sensorNames) {
2764 BMCWEB_LOG_DEBUG << "getChassisCb enter";
Ed Tanous81ce6092020-12-17 16:54:55 +00002765 processSensorList(sensorsAsyncResp, sensorNames);
Johnathan Mantey49c53ac2019-05-02 09:22:38 -07002766 BMCWEB_LOG_DEBUG << "getChassisCb exit";
2767 };
zhanghch058d1b46d2021-04-01 11:18:24 +08002768 sensorsAsyncResp->asyncResp->res.jsonValue["Redundancy"] =
2769 nlohmann::json::array();
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +01002770
Shawn McCarney26f03892019-05-03 13:20:24 -05002771 // Get set of sensors in chassis
Ed Tanous81ce6092020-12-17 16:54:55 +00002772 getChassis(sensorsAsyncResp, std::move(getChassisCb));
Ed Tanous1abe55e2018-09-05 08:30:59 -07002773 BMCWEB_LOG_DEBUG << "getChassisData exit";
Ed Tanous271584a2019-07-09 16:24:22 -07002774}
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +01002775
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +05302776/**
Johnathan Mantey49c53ac2019-05-02 09:22:38 -07002777 * @brief Find the requested sensorName in the list of all sensors supplied by
2778 * the chassis node
2779 *
2780 * @param sensorName The sensor name supplied in the PATCH request
2781 * @param sensorsList The list of sensors managed by the chassis node
2782 * @param sensorsModified The list of sensors that were found as a result of
2783 * repeated calls to this function
2784 */
Ed Tanous23a21a12020-07-25 04:45:05 +00002785inline bool findSensorNameUsingSensorPath(
Richard Marian Thomaiyar0a86feb2019-05-27 23:16:40 +05302786 std::string_view sensorName,
Johnathan Mantey49c53ac2019-05-02 09:22:38 -07002787 boost::container::flat_set<std::string>& sensorsList,
2788 boost::container::flat_set<std::string>& sensorsModified)
2789{
George Liu28aa8de2021-02-01 15:13:30 +08002790 for (auto& chassisSensor : sensorsList)
Johnathan Mantey49c53ac2019-05-02 09:22:38 -07002791 {
George Liu28aa8de2021-02-01 15:13:30 +08002792 sdbusplus::message::object_path path(chassisSensor);
Ed Tanousb00dcc22021-02-23 12:52:50 -08002793 std::string thisSensorName = path.filename();
George Liu28aa8de2021-02-01 15:13:30 +08002794 if (thisSensorName.empty())
Johnathan Mantey49c53ac2019-05-02 09:22:38 -07002795 {
Johnathan Mantey49c53ac2019-05-02 09:22:38 -07002796 continue;
2797 }
2798 if (thisSensorName == sensorName)
2799 {
2800 sensorsModified.emplace(chassisSensor);
2801 return true;
2802 }
2803 }
2804 return false;
2805}
2806
2807/**
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +05302808 * @brief Entry point for overriding sensor values of given sensor
2809 *
zhanghch058d1b46d2021-04-01 11:18:24 +08002810 * @param sensorAsyncResp response object
Carol Wang4bb3dc32019-10-17 18:15:02 +08002811 * @param allCollections Collections extract from sensors' request patch info
jayaprakash Mutyala91e130a2020-03-04 22:26:38 +00002812 * @param chassisSubNode Chassis Node for which the query has to happen
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +05302813 */
Ed Tanous23a21a12020-07-25 04:45:05 +00002814inline void setSensorsOverride(
Ed Tanousb5a76932020-09-29 16:16:58 -07002815 const std::shared_ptr<SensorsAsyncResp>& sensorAsyncResp,
Carol Wang4bb3dc32019-10-17 18:15:02 +08002816 std::unordered_map<std::string, std::vector<nlohmann::json>>&
jayaprakash Mutyala397fd612020-02-06 23:33:34 +00002817 allCollections)
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +05302818{
jayaprakash Mutyala70d1d0a2020-01-21 23:41:36 +00002819 BMCWEB_LOG_INFO << "setSensorsOverride for subNode"
Carol Wang4bb3dc32019-10-17 18:15:02 +08002820 << sensorAsyncResp->chassisSubNode << "\n";
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +05302821
Richard Marian Thomaiyarf65af9e2019-02-13 23:35:05 +05302822 const char* propertyValueName;
2823 std::unordered_map<std::string, std::pair<double, std::string>> overrideMap;
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +05302824 std::string memberId;
2825 double value;
Richard Marian Thomaiyarf65af9e2019-02-13 23:35:05 +05302826 for (auto& collectionItems : allCollections)
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +05302827 {
Richard Marian Thomaiyarf65af9e2019-02-13 23:35:05 +05302828 if (collectionItems.first == "Temperatures")
2829 {
2830 propertyValueName = "ReadingCelsius";
2831 }
2832 else if (collectionItems.first == "Fans")
2833 {
2834 propertyValueName = "Reading";
2835 }
2836 else
2837 {
2838 propertyValueName = "ReadingVolts";
2839 }
2840 for (auto& item : collectionItems.second)
2841 {
zhanghch058d1b46d2021-04-01 11:18:24 +08002842 if (!json_util::readJson(item, sensorAsyncResp->asyncResp->res,
2843 "MemberId", memberId, propertyValueName,
2844 value))
Richard Marian Thomaiyarf65af9e2019-02-13 23:35:05 +05302845 {
2846 return;
2847 }
2848 overrideMap.emplace(memberId,
2849 std::make_pair(value, collectionItems.first));
2850 }
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +05302851 }
Carol Wang4bb3dc32019-10-17 18:15:02 +08002852
Ed Tanousb5a76932020-09-29 16:16:58 -07002853 auto getChassisSensorListCb = [sensorAsyncResp, overrideMap](
2854 const std::shared_ptr<
2855 boost::container::flat_set<
2856 std::string>>& sensorsList) {
Johnathan Mantey49c53ac2019-05-02 09:22:38 -07002857 // Match sensor names in the PATCH request to those managed by the
2858 // chassis node
2859 const std::shared_ptr<boost::container::flat_set<std::string>>
2860 sensorNames =
2861 std::make_shared<boost::container::flat_set<std::string>>();
Richard Marian Thomaiyarf65af9e2019-02-13 23:35:05 +05302862 for (const auto& item : overrideMap)
2863 {
2864 const auto& sensor = item.first;
Johnathan Mantey49c53ac2019-05-02 09:22:38 -07002865 if (!findSensorNameUsingSensorPath(sensor, *sensorsList,
2866 *sensorNames))
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +05302867 {
Richard Marian Thomaiyarf65af9e2019-02-13 23:35:05 +05302868 BMCWEB_LOG_INFO << "Unable to find memberId " << item.first;
zhanghch058d1b46d2021-04-01 11:18:24 +08002869 messages::resourceNotFound(sensorAsyncResp->asyncResp->res,
Richard Marian Thomaiyarf65af9e2019-02-13 23:35:05 +05302870 item.second.second, item.first);
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +05302871 return;
2872 }
Richard Marian Thomaiyarf65af9e2019-02-13 23:35:05 +05302873 }
2874 // Get the connection to which the memberId belongs
2875 auto getObjectsWithConnectionCb =
2876 [sensorAsyncResp, overrideMap](
Ed Tanouscb13a392020-07-25 19:02:03 +00002877 const boost::container::flat_set<std::string>& /*connections*/,
Richard Marian Thomaiyarf65af9e2019-02-13 23:35:05 +05302878 const std::set<std::pair<std::string, std::string>>&
2879 objectsWithConnection) {
2880 if (objectsWithConnection.size() != overrideMap.size())
2881 {
2882 BMCWEB_LOG_INFO
2883 << "Unable to find all objects with proper connection "
2884 << objectsWithConnection.size() << " requested "
2885 << overrideMap.size() << "\n";
2886 messages::resourceNotFound(
zhanghch058d1b46d2021-04-01 11:18:24 +08002887 sensorAsyncResp->asyncResp->res,
Adrian Ambrożewicza0ec28b2020-04-10 14:47:28 +02002888 sensorAsyncResp->chassisSubNode ==
2889 sensors::node::thermal
Richard Marian Thomaiyarf65af9e2019-02-13 23:35:05 +05302890 ? "Temperatures"
2891 : "Voltages",
2892 "Count");
2893 return;
2894 }
2895 for (const auto& item : objectsWithConnection)
2896 {
George Liu28aa8de2021-02-01 15:13:30 +08002897 sdbusplus::message::object_path path(item.first);
2898 std::string sensorName = path.filename();
2899 if (sensorName.empty())
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +05302900 {
zhanghch058d1b46d2021-04-01 11:18:24 +08002901 messages::internalError(
2902 sensorAsyncResp->asyncResp->res);
Richard Marian Thomaiyarf65af9e2019-02-13 23:35:05 +05302903 return;
2904 }
Richard Marian Thomaiyarf65af9e2019-02-13 23:35:05 +05302905
2906 const auto& iterator = overrideMap.find(sensorName);
2907 if (iterator == overrideMap.end())
2908 {
2909 BMCWEB_LOG_INFO << "Unable to find sensor object"
2910 << item.first << "\n";
zhanghch058d1b46d2021-04-01 11:18:24 +08002911 messages::internalError(
2912 sensorAsyncResp->asyncResp->res);
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +05302913 return;
2914 }
2915 crow::connections::systemBus->async_method_call(
Richard Marian Thomaiyarf65af9e2019-02-13 23:35:05 +05302916 [sensorAsyncResp](const boost::system::error_code ec) {
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +05302917 if (ec)
2918 {
2919 BMCWEB_LOG_DEBUG
Richard Marian Thomaiyarf65af9e2019-02-13 23:35:05 +05302920 << "setOverrideValueStatus DBUS error: "
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +05302921 << ec;
zhanghch058d1b46d2021-04-01 11:18:24 +08002922 messages::internalError(
2923 sensorAsyncResp->asyncResp->res);
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +05302924 return;
2925 }
2926 },
Richard Marian Thomaiyarf65af9e2019-02-13 23:35:05 +05302927 item.second, item.first,
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +05302928 "org.freedesktop.DBus.Properties", "Set",
2929 "xyz.openbmc_project.Sensor.Value", "Value",
Patrick Williams19bd78d2020-05-13 17:38:24 -05002930 std::variant<double>(iterator->second.first));
Richard Marian Thomaiyarf65af9e2019-02-13 23:35:05 +05302931 }
2932 };
2933 // Get object with connection for the given sensor name
2934 getObjectsWithConnection(sensorAsyncResp, sensorNames,
2935 std::move(getObjectsWithConnectionCb));
2936 };
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +05302937 // get full sensor list for the given chassisId and cross verify the sensor.
2938 getChassis(sensorAsyncResp, std::move(getChassisSensorListCb));
2939}
2940
Adrian Ambrożewicza0ec28b2020-04-10 14:47:28 +02002941/**
2942 * @brief Retrieves mapping of Redfish URIs to sensor value property to D-Bus
2943 * path of the sensor.
2944 *
2945 * Function builds valid Redfish response for sensor query of given chassis and
2946 * node. It then builds metadata about Redfish<->D-Bus correlations and provides
2947 * it to caller in a callback.
2948 *
2949 * @param chassis Chassis for which retrieval should be performed
2950 * @param node Node (group) of sensors. See sensors::node for supported values
2951 * @param mapComplete Callback to be called with retrieval result
2952 */
Ed Tanous23a21a12020-07-25 04:45:05 +00002953inline void retrieveUriToDbusMap(const std::string& chassis,
2954 const std::string& node,
2955 SensorsAsyncResp::DataCompleteCb&& mapComplete)
Adrian Ambrożewicza0ec28b2020-04-10 14:47:28 +02002956{
Wludzik, Jozefc2bf7f92021-03-08 14:35:54 +00002957 auto pathIt = sensors::dbus::paths.find(node);
2958 if (pathIt == sensors::dbus::paths.end())
Adrian Ambrożewicza0ec28b2020-04-10 14:47:28 +02002959 {
2960 BMCWEB_LOG_ERROR << "Wrong node provided : " << node;
2961 mapComplete(boost::beast::http::status::bad_request, {});
2962 return;
2963 }
Krzysztof Grobelnyd51e0722021-04-16 13:15:21 +00002964
2965 auto res = std::make_shared<crow::Response>();
2966 auto asyncResp = std::make_shared<bmcweb::AsyncResp>(*res);
Adrian Ambrożewicza0ec28b2020-04-10 14:47:28 +02002967 auto callback =
Krzysztof Grobelnyd51e0722021-04-16 13:15:21 +00002968 [res, asyncResp, mapCompleteCb{std::move(mapComplete)}](
Adrian Ambrożewicza0ec28b2020-04-10 14:47:28 +02002969 const boost::beast::http::status status,
2970 const boost::container::flat_map<std::string, std::string>&
2971 uriToDbus) { mapCompleteCb(status, uriToDbus); };
2972
2973 auto resp = std::make_shared<SensorsAsyncResp>(
Krzysztof Grobelnyd51e0722021-04-16 13:15:21 +00002974 asyncResp, chassis, pathIt->second, node, std::move(callback));
Adrian Ambrożewicza0ec28b2020-04-10 14:47:28 +02002975 getChassisData(resp);
2976}
2977
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002978inline void requestRoutesSensorCollection(App& app)
Anthony Wilson95a3eca2019-06-11 10:44:47 -05002979{
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002980 BMCWEB_ROUTE(app, "/redfish/v1/Chassis/<str>/Sensors/")
Ed Tanous432a8902021-06-14 15:28:56 -07002981 .privileges({{"Login"}})
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002982 .methods(
2983 boost::beast::http::verb::get)([](const crow::Request&,
2984 const std::shared_ptr<
2985 bmcweb::AsyncResp>& aResp,
2986 const std::string& chassisId) {
2987 BMCWEB_LOG_DEBUG << "SensorCollection doGet enter";
Anthony Wilson95a3eca2019-06-11 10:44:47 -05002988
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002989 std::shared_ptr<SensorsAsyncResp> asyncResp =
2990 std::make_shared<SensorsAsyncResp>(
2991 aResp, chassisId,
2992 sensors::dbus::paths.at(sensors::node::sensors),
2993 sensors::node::sensors);
zhanghch058d1b46d2021-04-01 11:18:24 +08002994
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002995 auto getChassisCb =
2996 [asyncResp](
2997 const std::shared_ptr<
2998 boost::container::flat_set<std::string>>& sensorNames) {
2999 BMCWEB_LOG_DEBUG << "getChassisCb enter";
Anthony Wilson95a3eca2019-06-11 10:44:47 -05003000
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003001 nlohmann::json& entriesArray =
3002 asyncResp->asyncResp->res.jsonValue["Members"];
3003 for (auto& sensor : *sensorNames)
Anthony Wilson95a3eca2019-06-11 10:44:47 -05003004 {
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003005 BMCWEB_LOG_DEBUG << "Adding sensor: " << sensor;
Anthony Wilson95a3eca2019-06-11 10:44:47 -05003006
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003007 sdbusplus::message::object_path path(sensor);
3008 std::string sensorName = path.filename();
3009 if (sensorName.empty())
Anthony Wilson95a3eca2019-06-11 10:44:47 -05003010 {
3011 BMCWEB_LOG_ERROR << "Invalid sensor path: "
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003012 << sensor;
3013 messages::internalError(asyncResp->asyncResp->res);
3014 return;
Anthony Wilson95a3eca2019-06-11 10:44:47 -05003015 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003016 entriesArray.push_back(
3017 {{"@odata.id", "/redfish/v1/Chassis/" +
3018 asyncResp->chassisId + "/" +
3019 asyncResp->chassisSubNode + "/" +
3020 sensorName}});
3021 }
Anthony Wilson95a3eca2019-06-11 10:44:47 -05003022
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003023 asyncResp->asyncResp->res.jsonValue["Members@odata.count"] =
3024 entriesArray.size();
3025 BMCWEB_LOG_DEBUG << "getChassisCb exit";
3026 };
Anthony Wilson95a3eca2019-06-11 10:44:47 -05003027
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003028 // Get set of sensors in chassis
3029 getChassis(asyncResp, std::move(getChassisCb));
3030 BMCWEB_LOG_DEBUG << "SensorCollection doGet exit";
3031 });
3032}
Anthony Wilson95a3eca2019-06-11 10:44:47 -05003033
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003034inline void requestRoutesSensor(App& app)
3035{
3036 BMCWEB_ROUTE(app, "/redfish/v1/Chassis/<str>/Sensors/<str>/")
Ed Tanous432a8902021-06-14 15:28:56 -07003037 .privileges({{"Login"}})
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003038 .methods(
3039 boost::beast::http::verb::get)([](const crow::Request&,
3040 const std::shared_ptr<
3041 bmcweb::AsyncResp>& aResp,
3042 const std::string& chassisId,
3043 const std::string& sensorName) {
3044 BMCWEB_LOG_DEBUG << "Sensor doGet enter";
3045 std::shared_ptr<SensorsAsyncResp> asyncResp =
3046 std::make_shared<SensorsAsyncResp>(aResp, chassisId,
3047 std::vector<const char*>(),
3048 sensors::node::sensors);
Anthony Wilson95a3eca2019-06-11 10:44:47 -05003049
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003050 const std::array<const char*, 1> interfaces = {
3051 "xyz.openbmc_project.Sensor.Value"};
3052
3053 // Get a list of all of the sensors that implement Sensor.Value
3054 // and get the path and service name associated with the sensor
3055 crow::connections::systemBus->async_method_call(
3056 [asyncResp, sensorName](const boost::system::error_code ec,
3057 const GetSubTreeType& subtree) {
3058 BMCWEB_LOG_DEBUG << "respHandler1 enter";
3059 if (ec)
3060 {
3061 messages::internalError(asyncResp->asyncResp->res);
3062 BMCWEB_LOG_ERROR
3063 << "Sensor getSensorPaths resp_handler: "
3064 << "Dbus error " << ec;
3065 return;
3066 }
3067
3068 GetSubTreeType::const_iterator it = std::find_if(
3069 subtree.begin(), subtree.end(),
3070 [sensorName](
3071 const std::pair<
3072 std::string,
3073 std::vector<std::pair<
3074 std::string, std::vector<std::string>>>>&
3075 object) {
3076 sdbusplus::message::object_path path(object.first);
3077 std::string name = path.filename();
3078 if (name.empty())
3079 {
3080 BMCWEB_LOG_ERROR << "Invalid sensor path: "
3081 << object.first;
3082 return false;
3083 }
3084
3085 return name == sensorName;
3086 });
3087
3088 if (it == subtree.end())
3089 {
3090 BMCWEB_LOG_ERROR << "Could not find path for sensor: "
3091 << sensorName;
3092 messages::resourceNotFound(asyncResp->asyncResp->res,
3093 "Sensor", sensorName);
3094 return;
3095 }
3096 std::string_view sensorPath = (*it).first;
3097 BMCWEB_LOG_DEBUG << "Found sensor path for sensor '"
3098 << sensorName << "': " << sensorPath;
3099
3100 const std::shared_ptr<
3101 boost::container::flat_set<std::string>>
3102 sensorList = std::make_shared<
3103 boost::container::flat_set<std::string>>();
3104
3105 sensorList->emplace(sensorPath);
3106 processSensorList(asyncResp, sensorList);
3107 BMCWEB_LOG_DEBUG << "respHandler1 exit";
3108 },
3109 "xyz.openbmc_project.ObjectMapper",
3110 "/xyz/openbmc_project/object_mapper",
3111 "xyz.openbmc_project.ObjectMapper", "GetSubTree",
3112 "/xyz/openbmc_project/sensors", 2, interfaces);
3113 });
3114}
Anthony Wilson95a3eca2019-06-11 10:44:47 -05003115
Ed Tanous1abe55e2018-09-05 08:30:59 -07003116} // namespace redfish