xref: /openbmc/bmcweb/redfish-core/lib/sensors.hpp (revision 40e9b92ec19acffb46f83a6e55b18974da5d708e)
1 // SPDX-License-Identifier: Apache-2.0
2 // SPDX-FileCopyrightText: Copyright OpenBMC Authors
3 // SPDX-FileCopyrightText: Copyright 2018 Intel Corporation
4 #pragma once
5 
6 #include "app.hpp"
7 #include "dbus_singleton.hpp"
8 #include "dbus_utility.hpp"
9 #include "generated/enums/redundancy.hpp"
10 #include "generated/enums/resource.hpp"
11 #include "query.hpp"
12 #include "registries/privilege_registry.hpp"
13 #include "str_utility.hpp"
14 #include "utils/dbus_utils.hpp"
15 #include "utils/json_utils.hpp"
16 #include "utils/query_param.hpp"
17 #include "utils/sensor_utils.hpp"
18 
19 #include <boost/system/error_code.hpp>
20 #include <boost/url/format.hpp>
21 #include <sdbusplus/asio/property.hpp>
22 #include <sdbusplus/unpack_properties.hpp>
23 
24 #include <array>
25 #include <cmath>
26 #include <iterator>
27 #include <limits>
28 #include <map>
29 #include <ranges>
30 #include <set>
31 #include <string>
32 #include <string_view>
33 #include <utility>
34 #include <variant>
35 
36 namespace redfish
37 {
38 
39 namespace sensors
40 {
41 
42 // clang-format off
43 namespace dbus
44 {
45 constexpr auto powerPaths = std::to_array<std::string_view>({
46     "/xyz/openbmc_project/sensors/voltage",
47     "/xyz/openbmc_project/sensors/power"
48 });
49 
getSensorPaths()50 constexpr auto getSensorPaths(){
51     if constexpr(BMCWEB_REDFISH_NEW_POWERSUBSYSTEM_THERMALSUBSYSTEM){
52     return std::to_array<std::string_view>({
53         "/xyz/openbmc_project/sensors/power",
54         "/xyz/openbmc_project/sensors/current",
55         "/xyz/openbmc_project/sensors/airflow",
56         "/xyz/openbmc_project/sensors/humidity",
57         "/xyz/openbmc_project/sensors/voltage",
58         "/xyz/openbmc_project/sensors/fan_tach",
59         "/xyz/openbmc_project/sensors/temperature",
60         "/xyz/openbmc_project/sensors/fan_pwm",
61         "/xyz/openbmc_project/sensors/altitude",
62         "/xyz/openbmc_project/sensors/energy",
63         "/xyz/openbmc_project/sensors/utilization"});
64     } else {
65       return  std::to_array<std::string_view>({"/xyz/openbmc_project/sensors/power",
66         "/xyz/openbmc_project/sensors/current",
67         "/xyz/openbmc_project/sensors/airflow",
68         "/xyz/openbmc_project/sensors/humidity",
69         "/xyz/openbmc_project/sensors/utilization"});
70 }
71 }
72 
73 constexpr auto sensorPaths = getSensorPaths();
74 
75 constexpr auto thermalPaths = std::to_array<std::string_view>({
76     "/xyz/openbmc_project/sensors/fan_tach",
77     "/xyz/openbmc_project/sensors/temperature",
78     "/xyz/openbmc_project/sensors/fan_pwm"
79 });
80 
81 } // namespace dbus
82 // clang-format on
83 
84 constexpr std::string_view powerNodeStr = sensor_utils::chassisSubNodeToString(
85     sensor_utils::ChassisSubNode::powerNode);
86 constexpr std::string_view sensorsNodeStr =
87     sensor_utils::chassisSubNodeToString(
88         sensor_utils::ChassisSubNode::sensorsNode);
89 constexpr std::string_view thermalNodeStr =
90     sensor_utils::chassisSubNodeToString(
91         sensor_utils::ChassisSubNode::thermalNode);
92 
93 using sensorPair =
94     std::pair<std::string_view, std::span<const std::string_view>>;
95 static constexpr std::array<sensorPair, 3> paths = {
96     {{sensors::powerNodeStr, dbus::powerPaths},
97      {sensors::sensorsNodeStr, dbus::sensorPaths},
98      {sensors::thermalNodeStr, dbus::thermalPaths}}};
99 
100 } // namespace sensors
101 
102 /**
103  * SensorsAsyncResp
104  * Gathers data needed for response processing after async calls are done
105  */
106 class SensorsAsyncResp
107 {
108   public:
109     using DataCompleteCb = std::function<void(
110         const boost::beast::http::status status,
111         const std::map<std::string, std::string>& uriToDbus)>;
112 
113     struct SensorData
114     {
115         std::string name;
116         std::string uri;
117         std::string dbusPath;
118     };
119 
SensorsAsyncResp(const std::shared_ptr<bmcweb::AsyncResp> & asyncRespIn,const std::string & chassisIdIn,std::span<const std::string_view> typesIn,std::string_view subNode)120     SensorsAsyncResp(const std::shared_ptr<bmcweb::AsyncResp>& asyncRespIn,
121                      const std::string& chassisIdIn,
122                      std::span<const std::string_view> typesIn,
123                      std::string_view subNode) :
124         asyncResp(asyncRespIn), chassisId(chassisIdIn), types(typesIn),
125         chassisSubNode(subNode), efficientExpand(false)
126     {}
127 
128     // Store extra data about sensor mapping and return it in callback
SensorsAsyncResp(const std::shared_ptr<bmcweb::AsyncResp> & asyncRespIn,const std::string & chassisIdIn,std::span<const std::string_view> typesIn,std::string_view subNode,DataCompleteCb && creationComplete)129     SensorsAsyncResp(const std::shared_ptr<bmcweb::AsyncResp>& asyncRespIn,
130                      const std::string& chassisIdIn,
131                      std::span<const std::string_view> typesIn,
132                      std::string_view subNode,
133                      DataCompleteCb&& creationComplete) :
134         asyncResp(asyncRespIn), chassisId(chassisIdIn), types(typesIn),
135         chassisSubNode(subNode), efficientExpand(false),
136         metadata{std::vector<SensorData>()},
137         dataComplete{std::move(creationComplete)}
138     {}
139 
140     // sensor collections expand
SensorsAsyncResp(const std::shared_ptr<bmcweb::AsyncResp> & asyncRespIn,const std::string & chassisIdIn,std::span<const std::string_view> typesIn,const std::string_view & subNode,bool efficientExpandIn)141     SensorsAsyncResp(const std::shared_ptr<bmcweb::AsyncResp>& asyncRespIn,
142                      const std::string& chassisIdIn,
143                      std::span<const std::string_view> typesIn,
144                      const std::string_view& subNode, bool efficientExpandIn) :
145         asyncResp(asyncRespIn), chassisId(chassisIdIn), types(typesIn),
146         chassisSubNode(subNode), efficientExpand(efficientExpandIn)
147     {}
148 
~SensorsAsyncResp()149     ~SensorsAsyncResp()
150     {
151         if (asyncResp->res.result() ==
152             boost::beast::http::status::internal_server_error)
153         {
154             // Reset the json object to clear out any data that made it in
155             // before the error happened todo(ed) handle error condition with
156             // proper code
157             asyncResp->res.jsonValue = nlohmann::json::object();
158         }
159 
160         if (dataComplete && metadata)
161         {
162             std::map<std::string, std::string> map;
163             if (asyncResp->res.result() == boost::beast::http::status::ok)
164             {
165                 for (auto& sensor : *metadata)
166                 {
167                     map.emplace(sensor.uri, sensor.dbusPath);
168                 }
169             }
170             dataComplete(asyncResp->res.result(), map);
171         }
172     }
173 
174     SensorsAsyncResp(const SensorsAsyncResp&) = delete;
175     SensorsAsyncResp(SensorsAsyncResp&&) = delete;
176     SensorsAsyncResp& operator=(const SensorsAsyncResp&) = delete;
177     SensorsAsyncResp& operator=(SensorsAsyncResp&&) = delete;
178 
addMetadata(const nlohmann::json & sensorObject,const std::string & dbusPath)179     void addMetadata(const nlohmann::json& sensorObject,
180                      const std::string& dbusPath)
181     {
182         if (metadata)
183         {
184             metadata->emplace_back(SensorData{
185                 sensorObject["Name"], sensorObject["@odata.id"], dbusPath});
186         }
187     }
188 
updateUri(const std::string & name,const std::string & uri)189     void updateUri(const std::string& name, const std::string& uri)
190     {
191         if (metadata)
192         {
193             for (auto& sensor : *metadata)
194             {
195                 if (sensor.name == name)
196                 {
197                     sensor.uri = uri;
198                 }
199             }
200         }
201     }
202 
203     const std::shared_ptr<bmcweb::AsyncResp> asyncResp;
204     const std::string chassisId;
205     const std::span<const std::string_view> types;
206     const std::string chassisSubNode;
207     const bool efficientExpand;
208 
209   private:
210     std::optional<std::vector<SensorData>> metadata;
211     DataCompleteCb dataComplete;
212 };
213 
214 using InventoryItem = sensor_utils::InventoryItem;
215 
216 /**
217  * @brief Get objects with connection necessary for sensors
218  * @param SensorsAsyncResp Pointer to object holding response data
219  * @param sensorNames Sensors retrieved from chassis
220  * @param callback Callback for processing gathered connections
221  */
222 template <typename Callback>
getObjectsWithConnection(const std::shared_ptr<SensorsAsyncResp> & sensorsAsyncResp,const std::shared_ptr<std::set<std::string>> & sensorNames,Callback && callback)223 void getObjectsWithConnection(
224     const std::shared_ptr<SensorsAsyncResp>& sensorsAsyncResp,
225     const std::shared_ptr<std::set<std::string>>& sensorNames,
226     Callback&& callback)
227 {
228     BMCWEB_LOG_DEBUG("getObjectsWithConnection enter");
229     const std::string path = "/xyz/openbmc_project/sensors";
230     constexpr std::array<std::string_view, 1> interfaces = {
231         "xyz.openbmc_project.Sensor.Value"};
232 
233     // Make call to ObjectMapper to find all sensors objects
234     dbus::utility::getSubTree(
235         path, 2, interfaces,
236         [callback = std::forward<Callback>(callback), sensorsAsyncResp,
237          sensorNames](const boost::system::error_code& ec,
238                       const dbus::utility::MapperGetSubTreeResponse& subtree) {
239             // Response handler for parsing objects subtree
240             BMCWEB_LOG_DEBUG("getObjectsWithConnection resp_handler enter");
241             if (ec)
242             {
243                 messages::internalError(sensorsAsyncResp->asyncResp->res);
244                 BMCWEB_LOG_ERROR(
245                     "getObjectsWithConnection resp_handler: Dbus error {}", ec);
246                 return;
247             }
248 
249             BMCWEB_LOG_DEBUG("Found {} subtrees", subtree.size());
250 
251             // Make unique list of connections only for requested sensor types
252             // and found in the chassis
253             std::set<std::string> connections;
254             std::set<std::pair<std::string, std::string>> objectsWithConnection;
255 
256             BMCWEB_LOG_DEBUG("sensorNames list count: {}", sensorNames->size());
257             for (const std::string& tsensor : *sensorNames)
258             {
259                 BMCWEB_LOG_DEBUG("Sensor to find: {}", tsensor);
260             }
261 
262             for (const std::pair<std::string,
263                                  std::vector<std::pair<
264                                      std::string, std::vector<std::string>>>>&
265                      object : subtree)
266             {
267                 if (sensorNames->find(object.first) != sensorNames->end())
268                 {
269                     for (const std::pair<std::string, std::vector<std::string>>&
270                              objData : object.second)
271                     {
272                         BMCWEB_LOG_DEBUG("Adding connection: {}",
273                                          objData.first);
274                         connections.insert(objData.first);
275                         objectsWithConnection.insert(
276                             std::make_pair(object.first, objData.first));
277                     }
278                 }
279             }
280             BMCWEB_LOG_DEBUG("Found {} connections", connections.size());
281             callback(std::move(connections), std::move(objectsWithConnection));
282             BMCWEB_LOG_DEBUG("getObjectsWithConnection resp_handler exit");
283         });
284     BMCWEB_LOG_DEBUG("getObjectsWithConnection exit");
285 }
286 
287 /**
288  * @brief Create connections necessary for sensors
289  * @param SensorsAsyncResp Pointer to object holding response data
290  * @param sensorNames Sensors retrieved from chassis
291  * @param callback Callback for processing gathered connections
292  */
293 template <typename Callback>
getConnections(const std::shared_ptr<SensorsAsyncResp> & sensorsAsyncResp,const std::shared_ptr<std::set<std::string>> & sensorNames,Callback && callback)294 void getConnections(const std::shared_ptr<SensorsAsyncResp>& sensorsAsyncResp,
295                     const std::shared_ptr<std::set<std::string>>& sensorNames,
296                     Callback&& callback)
297 {
298     auto objectsWithConnectionCb =
299         [callback = std::forward<Callback>(callback)](
300             const std::set<std::string>& connections,
301             const std::set<std::pair<std::string, std::string>>&
302             /*objectsWithConnection*/) { callback(connections); };
303     getObjectsWithConnection(sensorsAsyncResp, sensorNames,
304                              std::move(objectsWithConnectionCb));
305 }
306 
307 /**
308  * @brief Shrinks the list of sensors for processing
309  * @param SensorsAysncResp  The class holding the Redfish response
310  * @param allSensors  A list of all the sensors associated to the
311  * chassis element (i.e. baseboard, front panel, etc...)
312  * @param activeSensors A list that is a reduction of the incoming
313  * allSensors list.  Eliminate Thermal sensors when a Power request is
314  * made, and eliminate Power sensors when a Thermal request is made.
315  */
reduceSensorList(crow::Response & res,std::string_view chassisSubNode,std::span<const std::string_view> sensorTypes,const std::vector<std::string> * allSensors,const std::shared_ptr<std::set<std::string>> & activeSensors)316 inline void reduceSensorList(
317     crow::Response& res, std::string_view chassisSubNode,
318     std::span<const std::string_view> sensorTypes,
319     const std::vector<std::string>* allSensors,
320     const std::shared_ptr<std::set<std::string>>& activeSensors)
321 {
322     if ((allSensors == nullptr) || (activeSensors == nullptr))
323     {
324         messages::resourceNotFound(res, chassisSubNode,
325                                    chassisSubNode == sensors::thermalNodeStr
326                                        ? "Temperatures"
327                                        : "Voltages");
328 
329         return;
330     }
331     if (allSensors->empty())
332     {
333         // Nothing to do, the activeSensors object is also empty
334         return;
335     }
336 
337     for (std::string_view type : sensorTypes)
338     {
339         for (const std::string& sensor : *allSensors)
340         {
341             if (sensor.starts_with(type))
342             {
343                 activeSensors->emplace(sensor);
344             }
345         }
346     }
347 }
348 
349 /*
350  *Populates the top level collection for a given subnode.  Populates
351  *SensorCollection, Power, or Thermal schemas.
352  *
353  * */
populateChassisNode(nlohmann::json & jsonValue,std::string_view chassisSubNode)354 inline void populateChassisNode(nlohmann::json& jsonValue,
355                                 std::string_view chassisSubNode)
356 {
357     if (chassisSubNode == sensors::powerNodeStr)
358     {
359         jsonValue["@odata.type"] = "#Power.v1_5_2.Power";
360     }
361     else if (chassisSubNode == sensors::thermalNodeStr)
362     {
363         jsonValue["@odata.type"] = "#Thermal.v1_4_0.Thermal";
364         jsonValue["Fans"] = nlohmann::json::array();
365         jsonValue["Temperatures"] = nlohmann::json::array();
366     }
367     else if (chassisSubNode == sensors::sensorsNodeStr)
368     {
369         jsonValue["@odata.type"] = "#SensorCollection.SensorCollection";
370         jsonValue["Description"] = "Collection of Sensors for this Chassis";
371         jsonValue["Members"] = nlohmann::json::array();
372         jsonValue["Members@odata.count"] = 0;
373     }
374 
375     if (chassisSubNode != sensors::sensorsNodeStr)
376     {
377         jsonValue["Id"] = chassisSubNode;
378     }
379     jsonValue["Name"] = chassisSubNode;
380 }
381 
382 /**
383  * @brief Retrieves requested chassis sensors and redundancy data from DBus .
384  * @param SensorsAsyncResp   Pointer to object holding response data
385  * @param callback  Callback for next step in gathered sensor processing
386  */
387 template <typename Callback>
getChassis(const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,std::string_view chassisId,std::string_view chassisSubNode,std::span<const std::string_view> sensorTypes,Callback && callback)388 void getChassis(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
389                 std::string_view chassisId, std::string_view chassisSubNode,
390                 std::span<const std::string_view> sensorTypes,
391                 Callback&& callback)
392 {
393     BMCWEB_LOG_DEBUG("getChassis enter");
394     constexpr std::array<std::string_view, 2> interfaces = {
395         "xyz.openbmc_project.Inventory.Item.Board",
396         "xyz.openbmc_project.Inventory.Item.Chassis"};
397 
398     // Get the Chassis Collection
399     dbus::utility::getSubTreePaths(
400         "/xyz/openbmc_project/inventory", 0, interfaces,
401         [callback = std::forward<Callback>(callback), asyncResp,
402          chassisIdStr{std::string(chassisId)},
403          chassisSubNode{std::string(chassisSubNode)},
404          sensorTypes](const boost::system::error_code& ec,
405                       const dbus::utility::MapperGetSubTreePathsResponse&
406                           chassisPaths) mutable {
407             BMCWEB_LOG_DEBUG("getChassis respHandler enter");
408             if (ec)
409             {
410                 BMCWEB_LOG_ERROR("getChassis respHandler DBUS error: {}", ec);
411                 messages::internalError(asyncResp->res);
412                 return;
413             }
414             const std::string* chassisPath = nullptr;
415             for (const std::string& chassis : chassisPaths)
416             {
417                 sdbusplus::message::object_path path(chassis);
418                 std::string chassisName = path.filename();
419                 if (chassisName.empty())
420                 {
421                     BMCWEB_LOG_ERROR("Failed to find '/' in {}", chassis);
422                     continue;
423                 }
424                 if (chassisName == chassisIdStr)
425                 {
426                     chassisPath = &chassis;
427                     break;
428                 }
429             }
430             if (chassisPath == nullptr)
431             {
432                 messages::resourceNotFound(asyncResp->res, "Chassis",
433                                            chassisIdStr);
434                 return;
435             }
436             populateChassisNode(asyncResp->res.jsonValue, chassisSubNode);
437 
438             asyncResp->res.jsonValue["@odata.id"] = boost::urls::format(
439                 "/redfish/v1/Chassis/{}/{}", chassisIdStr, chassisSubNode);
440 
441             // Get the list of all sensors for this Chassis element
442             std::string sensorPath = *chassisPath + "/all_sensors";
443             dbus::utility::getAssociationEndPoints(
444                 sensorPath, [asyncResp, chassisSubNode, sensorTypes,
445                              callback = std::forward<Callback>(callback)](
446                                 const boost::system::error_code& ec2,
447                                 const dbus::utility::MapperEndPoints&
448                                     nodeSensorList) mutable {
449                     if (ec2)
450                     {
451                         if (ec2.value() != EBADR)
452                         {
453                             messages::internalError(asyncResp->res);
454                             return;
455                         }
456                     }
457                     const std::shared_ptr<std::set<std::string>>
458                         culledSensorList =
459                             std::make_shared<std::set<std::string>>();
460                     reduceSensorList(asyncResp->res, chassisSubNode,
461                                      sensorTypes, &nodeSensorList,
462                                      culledSensorList);
463                     BMCWEB_LOG_DEBUG("Finishing with {}",
464                                      culledSensorList->size());
465                     callback(culledSensorList);
466                 });
467         });
468     BMCWEB_LOG_DEBUG("getChassis exit");
469 }
470 
471 /**
472  * @brief Builds a json sensor representation of a sensor.
473  * @param sensorName  The name of the sensor to be built
474  * @param sensorType  The type (temperature, fan_tach, etc) of the sensor to
475  * build
476  * @param chassisSubNode The subnode (thermal, sensor, etc) of the sensor
477  * @param interfacesDict  A dictionary of the interfaces and properties of said
478  * interfaces to be built from
479  * @param sensorJson  The json object to fill
480  * @param inventoryItem D-Bus inventory item associated with the sensor.  Will
481  * be nullptr if no associated inventory item was found.
482  */
objectInterfacesToJson(const std::string & sensorName,const std::string & sensorType,const sensor_utils::ChassisSubNode chassisSubNode,const dbus::utility::DBusInterfacesMap & interfacesDict,nlohmann::json & sensorJson,InventoryItem * inventoryItem)483 inline void objectInterfacesToJson(
484     const std::string& sensorName, const std::string& sensorType,
485     const sensor_utils::ChassisSubNode chassisSubNode,
486     const dbus::utility::DBusInterfacesMap& interfacesDict,
487     nlohmann::json& sensorJson, InventoryItem* inventoryItem)
488 {
489     for (const auto& [interface, valuesDict] : interfacesDict)
490     {
491         sensor_utils::objectPropertiesToJson(
492             sensorName, sensorType, chassisSubNode, valuesDict, sensorJson,
493             inventoryItem);
494     }
495     BMCWEB_LOG_DEBUG("Added sensor {}", sensorName);
496 }
497 
populateFanRedundancy(const std::shared_ptr<SensorsAsyncResp> & sensorsAsyncResp)498 inline void populateFanRedundancy(
499     const std::shared_ptr<SensorsAsyncResp>& sensorsAsyncResp)
500 {
501     constexpr std::array<std::string_view, 1> interfaces = {
502         "xyz.openbmc_project.Control.FanRedundancy"};
503     dbus::utility::getSubTree(
504         "/xyz/openbmc_project/control", 2, interfaces,
505         [sensorsAsyncResp](
506             const boost::system::error_code& ec,
507             const dbus::utility::MapperGetSubTreeResponse& resp) {
508             if (ec)
509             {
510                 return; // don't have to have this interface
511             }
512             for (const std::pair<std::string, dbus::utility::MapperServiceMap>&
513                      pathPair : resp)
514             {
515                 const std::string& path = pathPair.first;
516                 const dbus::utility::MapperServiceMap& objDict =
517                     pathPair.second;
518                 if (objDict.empty())
519                 {
520                     continue; // this should be impossible
521                 }
522 
523                 const std::string& owner = objDict.begin()->first;
524                 dbus::utility::getAssociationEndPoints(
525                     path + "/chassis",
526                     [path, owner, sensorsAsyncResp](
527                         const boost::system::error_code& ec2,
528                         const dbus::utility::MapperEndPoints& endpoints) {
529                         if (ec2)
530                         {
531                             return; // if they don't have an association we
532                                     // can't tell what chassis is
533                         }
534                         auto found = std::ranges::find_if(
535                             endpoints,
536                             [sensorsAsyncResp](const std::string& entry) {
537                                 return entry.find(
538                                            sensorsAsyncResp->chassisId) !=
539                                        std::string::npos;
540                             });
541 
542                         if (found == endpoints.end())
543                         {
544                             return;
545                         }
546                         dbus::utility::getAllProperties(
547                             *crow::connections::systemBus, owner, path,
548                             "xyz.openbmc_project.Control.FanRedundancy",
549                             [path, sensorsAsyncResp](
550                                 const boost::system::error_code& ec3,
551                                 const dbus::utility::DBusPropertiesMap& ret) {
552                                 if (ec3)
553                                 {
554                                     return; // don't have to have this
555                                             // interface
556                                 }
557 
558                                 const uint8_t* allowedFailures = nullptr;
559                                 const std::vector<std::string>* collection =
560                                     nullptr;
561                                 const std::string* status = nullptr;
562 
563                                 const bool success =
564                                     sdbusplus::unpackPropertiesNoThrow(
565                                         dbus_utils::UnpackErrorPrinter(), ret,
566                                         "AllowedFailures", allowedFailures,
567                                         "Collection", collection, "Status",
568                                         status);
569 
570                                 if (!success)
571                                 {
572                                     messages::internalError(
573                                         sensorsAsyncResp->asyncResp->res);
574                                     return;
575                                 }
576 
577                                 if (allowedFailures == nullptr ||
578                                     collection == nullptr || status == nullptr)
579                                 {
580                                     BMCWEB_LOG_ERROR(
581                                         "Invalid redundancy interface");
582                                     messages::internalError(
583                                         sensorsAsyncResp->asyncResp->res);
584                                     return;
585                                 }
586 
587                                 sdbusplus::message::object_path objectPath(
588                                     path);
589                                 std::string name = objectPath.filename();
590                                 if (name.empty())
591                                 {
592                                     // this should be impossible
593                                     messages::internalError(
594                                         sensorsAsyncResp->asyncResp->res);
595                                     return;
596                                 }
597                                 std::ranges::replace(name, '_', ' ');
598 
599                                 std::string health;
600 
601                                 if (status->ends_with("Full"))
602                                 {
603                                     health = "OK";
604                                 }
605                                 else if (status->ends_with("Degraded"))
606                                 {
607                                     health = "Warning";
608                                 }
609                                 else
610                                 {
611                                     health = "Critical";
612                                 }
613                                 nlohmann::json::array_t redfishCollection;
614                                 const auto& fanRedfish =
615                                     sensorsAsyncResp->asyncResp->res
616                                         .jsonValue["Fans"];
617                                 for (const std::string& item : *collection)
618                                 {
619                                     sdbusplus::message::object_path itemPath(
620                                         item);
621                                     std::string itemName = itemPath.filename();
622                                     if (itemName.empty())
623                                     {
624                                         continue;
625                                     }
626                                     /*
627                                     todo(ed): merge patch that fixes the names
628                                     std::replace(itemName.begin(),
629                                                  itemName.end(), '_', ' ');*/
630                                     auto schemaItem = std::ranges::find_if(
631                                         fanRedfish,
632                                         [itemName](const nlohmann::json& fan) {
633                                             return fan["Name"] == itemName;
634                                         });
635                                     if (schemaItem != fanRedfish.end())
636                                     {
637                                         nlohmann::json::object_t collectionId;
638                                         collectionId["@odata.id"] =
639                                             (*schemaItem)["@odata.id"];
640                                         redfishCollection.emplace_back(
641                                             std::move(collectionId));
642                                     }
643                                     else
644                                     {
645                                         BMCWEB_LOG_ERROR(
646                                             "failed to find fan in schema");
647                                         messages::internalError(
648                                             sensorsAsyncResp->asyncResp->res);
649                                         return;
650                                     }
651                                 }
652 
653                                 size_t minNumNeeded =
654                                     collection->empty()
655                                         ? 0
656                                         : collection->size() - *allowedFailures;
657                                 nlohmann::json& jResp =
658                                     sensorsAsyncResp->asyncResp->res
659                                         .jsonValue["Redundancy"];
660 
661                                 nlohmann::json::object_t redundancy;
662                                 boost::urls::url url = boost::urls::format(
663                                     "/redfish/v1/Chassis/{}/{}",
664                                     sensorsAsyncResp->chassisId,
665                                     sensorsAsyncResp->chassisSubNode);
666                                 url.set_fragment(
667                                     ("/Redundancy"_json_pointer / jResp.size())
668                                         .to_string());
669                                 redundancy["@odata.id"] = std::move(url);
670                                 redundancy["@odata.type"] =
671                                     "#Redundancy.v1_3_2.Redundancy";
672                                 redundancy["MinNumNeeded"] = minNumNeeded;
673                                 redundancy["Mode"] =
674                                     redundancy::RedundancyType::NPlusM;
675                                 redundancy["Name"] = name;
676                                 redundancy["RedundancySet"] = redfishCollection;
677                                 redundancy["Status"]["Health"] = health;
678                                 redundancy["Status"]["State"] =
679                                     resource::State::Enabled;
680 
681                                 jResp.emplace_back(std::move(redundancy));
682                             });
683                     });
684             }
685         });
686 }
687 
688 inline void
sortJSONResponse(const std::shared_ptr<SensorsAsyncResp> & sensorsAsyncResp)689     sortJSONResponse(const std::shared_ptr<SensorsAsyncResp>& sensorsAsyncResp)
690 {
691     nlohmann::json& response = sensorsAsyncResp->asyncResp->res.jsonValue;
692     std::array<std::string, 2> sensorHeaders{"Temperatures", "Fans"};
693     if (sensorsAsyncResp->chassisSubNode == sensors::powerNodeStr)
694     {
695         sensorHeaders = {"Voltages", "PowerSupplies"};
696     }
697     for (const std::string& sensorGroup : sensorHeaders)
698     {
699         nlohmann::json::iterator entry = response.find(sensorGroup);
700         if (entry == response.end())
701         {
702             continue;
703         }
704         nlohmann::json::array_t* arr =
705             entry->get_ptr<nlohmann::json::array_t*>();
706         if (arr == nullptr)
707         {
708             continue;
709         }
710         json_util::sortJsonArrayByKey(*arr, "Name");
711 
712         // add the index counts to the end of each entry
713         size_t count = 0;
714         for (nlohmann::json& sensorJson : *entry)
715         {
716             nlohmann::json::iterator odata = sensorJson.find("@odata.id");
717             if (odata == sensorJson.end())
718             {
719                 continue;
720             }
721             std::string* value = odata->get_ptr<std::string*>();
722             if (value != nullptr)
723             {
724                 *value += "/" + std::to_string(count);
725                 sensorJson["MemberId"] = std::to_string(count);
726                 count++;
727                 sensorsAsyncResp->updateUri(sensorJson["Name"], *value);
728             }
729         }
730     }
731 }
732 
733 /**
734  * @brief Finds the inventory item with the specified object path.
735  * @param inventoryItems D-Bus inventory items associated with sensors.
736  * @param invItemObjPath D-Bus object path of inventory item.
737  * @return Inventory item within vector, or nullptr if no match found.
738  */
findInventoryItem(const std::shared_ptr<std::vector<InventoryItem>> & inventoryItems,const std::string & invItemObjPath)739 inline InventoryItem* findInventoryItem(
740     const std::shared_ptr<std::vector<InventoryItem>>& inventoryItems,
741     const std::string& invItemObjPath)
742 {
743     for (InventoryItem& inventoryItem : *inventoryItems)
744     {
745         if (inventoryItem.objectPath == invItemObjPath)
746         {
747             return &inventoryItem;
748         }
749     }
750     return nullptr;
751 }
752 
753 /**
754  * @brief Finds the inventory item associated with the specified sensor.
755  * @param inventoryItems D-Bus inventory items associated with sensors.
756  * @param sensorObjPath D-Bus object path of sensor.
757  * @return Inventory item within vector, or nullptr if no match found.
758  */
findInventoryItemForSensor(const std::shared_ptr<std::vector<InventoryItem>> & inventoryItems,const std::string & sensorObjPath)759 inline InventoryItem* findInventoryItemForSensor(
760     const std::shared_ptr<std::vector<InventoryItem>>& inventoryItems,
761     const std::string& sensorObjPath)
762 {
763     for (InventoryItem& inventoryItem : *inventoryItems)
764     {
765         if (inventoryItem.sensors.contains(sensorObjPath))
766         {
767             return &inventoryItem;
768         }
769     }
770     return nullptr;
771 }
772 
773 /**
774  * @brief Finds the inventory item associated with the specified led path.
775  * @param inventoryItems D-Bus inventory items associated with sensors.
776  * @param ledObjPath D-Bus object path of led.
777  * @return Inventory item within vector, or nullptr if no match found.
778  */
findInventoryItemForLed(std::vector<InventoryItem> & inventoryItems,const std::string & ledObjPath)779 inline InventoryItem* findInventoryItemForLed(
780     std::vector<InventoryItem>& inventoryItems, const std::string& ledObjPath)
781 {
782     for (InventoryItem& inventoryItem : inventoryItems)
783     {
784         if (inventoryItem.ledObjectPath == ledObjPath)
785         {
786             return &inventoryItem;
787         }
788     }
789     return nullptr;
790 }
791 
792 /**
793  * @brief Adds inventory item and associated sensor to specified vector.
794  *
795  * Adds a new InventoryItem to the vector if necessary.  Searches for an
796  * existing InventoryItem with the specified object path.  If not found, one is
797  * added to the vector.
798  *
799  * Next, the specified sensor is added to the set of sensors associated with the
800  * InventoryItem.
801  *
802  * @param inventoryItems D-Bus inventory items associated with sensors.
803  * @param invItemObjPath D-Bus object path of inventory item.
804  * @param sensorObjPath D-Bus object path of sensor
805  */
addInventoryItem(const std::shared_ptr<std::vector<InventoryItem>> & inventoryItems,const std::string & invItemObjPath,const std::string & sensorObjPath)806 inline void addInventoryItem(
807     const std::shared_ptr<std::vector<InventoryItem>>& inventoryItems,
808     const std::string& invItemObjPath, const std::string& sensorObjPath)
809 {
810     // Look for inventory item in vector
811     InventoryItem* inventoryItem =
812         findInventoryItem(inventoryItems, invItemObjPath);
813 
814     // If inventory item doesn't exist in vector, add it
815     if (inventoryItem == nullptr)
816     {
817         inventoryItems->emplace_back(invItemObjPath);
818         inventoryItem = &(inventoryItems->back());
819     }
820 
821     // Add sensor to set of sensors associated with inventory item
822     inventoryItem->sensors.emplace(sensorObjPath);
823 }
824 
825 /**
826  * @brief Stores D-Bus data in the specified inventory item.
827  *
828  * Finds D-Bus data in the specified map of interfaces.  Stores the data in the
829  * specified InventoryItem.
830  *
831  * This data is later used to provide sensor property values in the JSON
832  * response.
833  *
834  * @param inventoryItem Inventory item where data will be stored.
835  * @param interfacesDict Map containing D-Bus interfaces and their properties
836  * for the specified inventory item.
837  */
storeInventoryItemData(InventoryItem & inventoryItem,const dbus::utility::DBusInterfacesMap & interfacesDict)838 inline void storeInventoryItemData(
839     InventoryItem& inventoryItem,
840     const dbus::utility::DBusInterfacesMap& interfacesDict)
841 {
842     // Get properties from Inventory.Item interface
843 
844     for (const auto& [interface, values] : interfacesDict)
845     {
846         if (interface == "xyz.openbmc_project.Inventory.Item")
847         {
848             for (const auto& [name, dbusValue] : values)
849             {
850                 if (name == "Present")
851                 {
852                     const bool* value = std::get_if<bool>(&dbusValue);
853                     if (value != nullptr)
854                     {
855                         inventoryItem.isPresent = *value;
856                     }
857                 }
858             }
859         }
860         // Check if Inventory.Item.PowerSupply interface is present
861 
862         if (interface == "xyz.openbmc_project.Inventory.Item.PowerSupply")
863         {
864             inventoryItem.isPowerSupply = true;
865         }
866 
867         // Get properties from Inventory.Decorator.Asset interface
868         if (interface == "xyz.openbmc_project.Inventory.Decorator.Asset")
869         {
870             for (const auto& [name, dbusValue] : values)
871             {
872                 if (name == "Manufacturer")
873                 {
874                     const std::string* value =
875                         std::get_if<std::string>(&dbusValue);
876                     if (value != nullptr)
877                     {
878                         inventoryItem.manufacturer = *value;
879                     }
880                 }
881                 if (name == "Model")
882                 {
883                     const std::string* value =
884                         std::get_if<std::string>(&dbusValue);
885                     if (value != nullptr)
886                     {
887                         inventoryItem.model = *value;
888                     }
889                 }
890                 if (name == "SerialNumber")
891                 {
892                     const std::string* value =
893                         std::get_if<std::string>(&dbusValue);
894                     if (value != nullptr)
895                     {
896                         inventoryItem.serialNumber = *value;
897                     }
898                 }
899                 if (name == "PartNumber")
900                 {
901                     const std::string* value =
902                         std::get_if<std::string>(&dbusValue);
903                     if (value != nullptr)
904                     {
905                         inventoryItem.partNumber = *value;
906                     }
907                 }
908             }
909         }
910 
911         if (interface ==
912             "xyz.openbmc_project.State.Decorator.OperationalStatus")
913         {
914             for (const auto& [name, dbusValue] : values)
915             {
916                 if (name == "Functional")
917                 {
918                     const bool* value = std::get_if<bool>(&dbusValue);
919                     if (value != nullptr)
920                     {
921                         inventoryItem.isFunctional = *value;
922                     }
923                 }
924             }
925         }
926     }
927 }
928 
929 /**
930  * @brief Gets D-Bus data for inventory items associated with sensors.
931  *
932  * Uses the specified connections (services) to obtain D-Bus data for inventory
933  * items associated with sensors.  Stores the resulting data in the
934  * inventoryItems vector.
935  *
936  * This data is later used to provide sensor property values in the JSON
937  * response.
938  *
939  * Finds the inventory item data asynchronously.  Invokes callback when data has
940  * been obtained.
941  *
942  * The callback must have the following signature:
943  *   @code
944  *   callback(void)
945  *   @endcode
946  *
947  * This function is called recursively, obtaining data asynchronously from one
948  * connection in each call.  This ensures the callback is not invoked until the
949  * last asynchronous function has completed.
950  *
951  * @param sensorsAsyncResp Pointer to object holding response data.
952  * @param inventoryItems D-Bus inventory items associated with sensors.
953  * @param invConnections Connections that provide data for the inventory items.
954  * implements ObjectManager.
955  * @param callback Callback to invoke when inventory data has been obtained.
956  * @param invConnectionsIndex Current index in invConnections.  Only specified
957  * in recursive calls to this function.
958  */
959 template <typename Callback>
getInventoryItemsData(const std::shared_ptr<SensorsAsyncResp> & sensorsAsyncResp,const std::shared_ptr<std::vector<InventoryItem>> & inventoryItems,const std::shared_ptr<std::set<std::string>> & invConnections,Callback && callback,size_t invConnectionsIndex=0)960 void getInventoryItemsData(
961     const std::shared_ptr<SensorsAsyncResp>& sensorsAsyncResp,
962     const std::shared_ptr<std::vector<InventoryItem>>& inventoryItems,
963     const std::shared_ptr<std::set<std::string>>& invConnections,
964     Callback&& callback, size_t invConnectionsIndex = 0)
965 {
966     BMCWEB_LOG_DEBUG("getInventoryItemsData enter");
967 
968     // If no more connections left, call callback
969     if (invConnectionsIndex >= invConnections->size())
970     {
971         callback();
972         BMCWEB_LOG_DEBUG("getInventoryItemsData exit");
973         return;
974     }
975 
976     // Get inventory item data from current connection
977     auto it = invConnections->begin();
978     std::advance(it, invConnectionsIndex);
979     if (it != invConnections->end())
980     {
981         const std::string& invConnection = *it;
982 
983         // Get all object paths and their interfaces for current connection
984         sdbusplus::message::object_path path("/xyz/openbmc_project/inventory");
985         dbus::utility::getManagedObjects(
986             invConnection, path,
987             [sensorsAsyncResp, inventoryItems, invConnections,
988              callback = std::forward<Callback>(callback), invConnectionsIndex](
989                 const boost::system::error_code& ec,
990                 const dbus::utility::ManagedObjectType& resp) mutable {
991                 BMCWEB_LOG_DEBUG("getInventoryItemsData respHandler enter");
992                 if (ec)
993                 {
994                     BMCWEB_LOG_ERROR(
995                         "getInventoryItemsData respHandler DBus error {}", ec);
996                     messages::internalError(sensorsAsyncResp->asyncResp->res);
997                     return;
998                 }
999 
1000                 // Loop through returned object paths
1001                 for (const auto& objDictEntry : resp)
1002                 {
1003                     const std::string& objPath =
1004                         static_cast<const std::string&>(objDictEntry.first);
1005 
1006                     // If this object path is one of the specified inventory
1007                     // items
1008                     InventoryItem* inventoryItem =
1009                         findInventoryItem(inventoryItems, objPath);
1010                     if (inventoryItem != nullptr)
1011                     {
1012                         // Store inventory data in InventoryItem
1013                         storeInventoryItemData(*inventoryItem,
1014                                                objDictEntry.second);
1015                     }
1016                 }
1017 
1018                 // Recurse to get inventory item data from next connection
1019                 getInventoryItemsData(sensorsAsyncResp, inventoryItems,
1020                                       invConnections, std::move(callback),
1021                                       invConnectionsIndex + 1);
1022 
1023                 BMCWEB_LOG_DEBUG("getInventoryItemsData respHandler exit");
1024             });
1025     }
1026 
1027     BMCWEB_LOG_DEBUG("getInventoryItemsData exit");
1028 }
1029 
1030 /**
1031  * @brief Gets connections that provide D-Bus data for inventory items.
1032  *
1033  * Gets the D-Bus connections (services) that provide data for the inventory
1034  * items that are associated with sensors.
1035  *
1036  * Finds the connections asynchronously.  Invokes callback when information has
1037  * been obtained.
1038  *
1039  * The callback must have the following signature:
1040  *   @code
1041  *   callback(std::shared_ptr<std::set<std::string>> invConnections)
1042  *   @endcode
1043  *
1044  * @param sensorsAsyncResp Pointer to object holding response data.
1045  * @param inventoryItems D-Bus inventory items associated with sensors.
1046  * @param callback Callback to invoke when connections have been obtained.
1047  */
1048 template <typename Callback>
getInventoryItemsConnections(const std::shared_ptr<SensorsAsyncResp> & sensorsAsyncResp,const std::shared_ptr<std::vector<InventoryItem>> & inventoryItems,Callback && callback)1049 void getInventoryItemsConnections(
1050     const std::shared_ptr<SensorsAsyncResp>& sensorsAsyncResp,
1051     const std::shared_ptr<std::vector<InventoryItem>>& inventoryItems,
1052     Callback&& callback)
1053 {
1054     BMCWEB_LOG_DEBUG("getInventoryItemsConnections enter");
1055 
1056     const std::string path = "/xyz/openbmc_project/inventory";
1057     constexpr std::array<std::string_view, 4> interfaces = {
1058         "xyz.openbmc_project.Inventory.Item",
1059         "xyz.openbmc_project.Inventory.Item.PowerSupply",
1060         "xyz.openbmc_project.Inventory.Decorator.Asset",
1061         "xyz.openbmc_project.State.Decorator.OperationalStatus"};
1062 
1063     // Make call to ObjectMapper to find all inventory items
1064     dbus::utility::getSubTree(
1065         path, 0, interfaces,
1066         [callback = std::forward<Callback>(callback), sensorsAsyncResp,
1067          inventoryItems](
1068             const boost::system::error_code& ec,
1069             const dbus::utility::MapperGetSubTreeResponse& subtree) mutable {
1070             // Response handler for parsing output from GetSubTree
1071             BMCWEB_LOG_DEBUG("getInventoryItemsConnections respHandler enter");
1072             if (ec)
1073             {
1074                 messages::internalError(sensorsAsyncResp->asyncResp->res);
1075                 BMCWEB_LOG_ERROR(
1076                     "getInventoryItemsConnections respHandler DBus error {}",
1077                     ec);
1078                 return;
1079             }
1080 
1081             // Make unique list of connections for desired inventory items
1082             std::shared_ptr<std::set<std::string>> invConnections =
1083                 std::make_shared<std::set<std::string>>();
1084 
1085             // Loop through objects from GetSubTree
1086             for (const std::pair<std::string,
1087                                  std::vector<std::pair<
1088                                      std::string, std::vector<std::string>>>>&
1089                      object : subtree)
1090             {
1091                 // Check if object path is one of the specified inventory items
1092                 const std::string& objPath = object.first;
1093                 if (findInventoryItem(inventoryItems, objPath) != nullptr)
1094                 {
1095                     // Store all connections to inventory item
1096                     for (const std::pair<std::string, std::vector<std::string>>&
1097                              objData : object.second)
1098                     {
1099                         const std::string& invConnection = objData.first;
1100                         invConnections->insert(invConnection);
1101                     }
1102                 }
1103             }
1104 
1105             callback(invConnections);
1106             BMCWEB_LOG_DEBUG("getInventoryItemsConnections respHandler exit");
1107         });
1108     BMCWEB_LOG_DEBUG("getInventoryItemsConnections exit");
1109 }
1110 
1111 /**
1112  * @brief Gets associations from sensors to inventory items.
1113  *
1114  * Looks for ObjectMapper associations from the specified sensors to related
1115  * inventory items. Then finds the associations from those inventory items to
1116  * their LEDs, if any.
1117  *
1118  * Finds the inventory items asynchronously.  Invokes callback when information
1119  * has been obtained.
1120  *
1121  * The callback must have the following signature:
1122  *   @code
1123  *   callback(std::shared_ptr<std::vector<InventoryItem>> inventoryItems)
1124  *   @endcode
1125  *
1126  * @param sensorsAsyncResp Pointer to object holding response data.
1127  * @param sensorNames All sensors within the current chassis.
1128  * implements ObjectManager.
1129  * @param callback Callback to invoke when inventory items have been obtained.
1130  */
1131 template <typename Callback>
getInventoryItemAssociations(const std::shared_ptr<SensorsAsyncResp> & sensorsAsyncResp,const std::shared_ptr<std::set<std::string>> & sensorNames,Callback && callback)1132 void getInventoryItemAssociations(
1133     const std::shared_ptr<SensorsAsyncResp>& sensorsAsyncResp,
1134     const std::shared_ptr<std::set<std::string>>& sensorNames,
1135     Callback&& callback)
1136 {
1137     BMCWEB_LOG_DEBUG("getInventoryItemAssociations enter");
1138 
1139     // Call GetManagedObjects on the ObjectMapper to get all associations
1140     sdbusplus::message::object_path path("/");
1141     dbus::utility::getManagedObjects(
1142         "xyz.openbmc_project.ObjectMapper", path,
1143         [callback = std::forward<Callback>(callback), sensorsAsyncResp,
1144          sensorNames](const boost::system::error_code& ec,
1145                       const dbus::utility::ManagedObjectType& resp) mutable {
1146             BMCWEB_LOG_DEBUG("getInventoryItemAssociations respHandler enter");
1147             if (ec)
1148             {
1149                 BMCWEB_LOG_ERROR(
1150                     "getInventoryItemAssociations respHandler DBus error {}",
1151                     ec);
1152                 messages::internalError(sensorsAsyncResp->asyncResp->res);
1153                 return;
1154             }
1155 
1156             // Create vector to hold list of inventory items
1157             std::shared_ptr<std::vector<InventoryItem>> inventoryItems =
1158                 std::make_shared<std::vector<InventoryItem>>();
1159 
1160             // Loop through returned object paths
1161             std::string sensorAssocPath;
1162             sensorAssocPath.reserve(128); // avoid memory allocations
1163             for (const auto& objDictEntry : resp)
1164             {
1165                 const std::string& objPath =
1166                     static_cast<const std::string&>(objDictEntry.first);
1167 
1168                 // If path is inventory association for one of the specified
1169                 // sensors
1170                 for (const std::string& sensorName : *sensorNames)
1171                 {
1172                     sensorAssocPath = sensorName;
1173                     sensorAssocPath += "/inventory";
1174                     if (objPath == sensorAssocPath)
1175                     {
1176                         // Get Association interface for object path
1177                         for (const auto& [interface, values] :
1178                              objDictEntry.second)
1179                         {
1180                             if (interface == "xyz.openbmc_project.Association")
1181                             {
1182                                 for (const auto& [valueName, value] : values)
1183                                 {
1184                                     if (valueName == "endpoints")
1185                                     {
1186                                         const std::vector<std::string>*
1187                                             endpoints = std::get_if<
1188                                                 std::vector<std::string>>(
1189                                                 &value);
1190                                         if ((endpoints != nullptr) &&
1191                                             !endpoints->empty())
1192                                         {
1193                                             // Add inventory item to vector
1194                                             const std::string& invItemPath =
1195                                                 endpoints->front();
1196                                             addInventoryItem(inventoryItems,
1197                                                              invItemPath,
1198                                                              sensorName);
1199                                         }
1200                                     }
1201                                 }
1202                             }
1203                         }
1204                         break;
1205                     }
1206                 }
1207             }
1208 
1209             // Now loop through the returned object paths again, this time to
1210             // find the leds associated with the inventory items we just found
1211             std::string inventoryAssocPath;
1212             inventoryAssocPath.reserve(128); // avoid memory allocations
1213             for (const auto& objDictEntry : resp)
1214             {
1215                 const std::string& objPath =
1216                     static_cast<const std::string&>(objDictEntry.first);
1217 
1218                 for (InventoryItem& inventoryItem : *inventoryItems)
1219                 {
1220                     inventoryAssocPath = inventoryItem.objectPath;
1221                     inventoryAssocPath += "/leds";
1222                     if (objPath == inventoryAssocPath)
1223                     {
1224                         for (const auto& [interface, values] :
1225                              objDictEntry.second)
1226                         {
1227                             if (interface == "xyz.openbmc_project.Association")
1228                             {
1229                                 for (const auto& [valueName, value] : values)
1230                                 {
1231                                     if (valueName == "endpoints")
1232                                     {
1233                                         const std::vector<std::string>*
1234                                             endpoints = std::get_if<
1235                                                 std::vector<std::string>>(
1236                                                 &value);
1237                                         if ((endpoints != nullptr) &&
1238                                             !endpoints->empty())
1239                                         {
1240                                             // Add inventory item to vector
1241                                             // Store LED path in inventory item
1242                                             const std::string& ledPath =
1243                                                 endpoints->front();
1244                                             inventoryItem.ledObjectPath =
1245                                                 ledPath;
1246                                         }
1247                                     }
1248                                 }
1249                             }
1250                         }
1251 
1252                         break;
1253                     }
1254                 }
1255             }
1256             callback(inventoryItems);
1257             BMCWEB_LOG_DEBUG("getInventoryItemAssociations respHandler exit");
1258         });
1259 
1260     BMCWEB_LOG_DEBUG("getInventoryItemAssociations exit");
1261 }
1262 
1263 /**
1264  * @brief Gets D-Bus data for inventory item leds associated with sensors.
1265  *
1266  * Uses the specified connections (services) to obtain D-Bus data for inventory
1267  * item leds associated with sensors.  Stores the resulting data in the
1268  * inventoryItems vector.
1269  *
1270  * This data is later used to provide sensor property values in the JSON
1271  * response.
1272  *
1273  * Finds the inventory item led data asynchronously.  Invokes callback when data
1274  * has been obtained.
1275  *
1276  * The callback must have the following signature:
1277  *   @code
1278  *   callback()
1279  *   @endcode
1280  *
1281  * This function is called recursively, obtaining data asynchronously from one
1282  * connection in each call.  This ensures the callback is not invoked until the
1283  * last asynchronous function has completed.
1284  *
1285  * @param sensorsAsyncResp Pointer to object holding response data.
1286  * @param inventoryItems D-Bus inventory items associated with sensors.
1287  * @param ledConnections Connections that provide data for the inventory leds.
1288  * @param callback Callback to invoke when inventory data has been obtained.
1289  * @param ledConnectionsIndex Current index in ledConnections.  Only specified
1290  * in recursive calls to this function.
1291  */
1292 template <typename Callback>
getInventoryLedData(const std::shared_ptr<SensorsAsyncResp> & sensorsAsyncResp,const std::shared_ptr<std::vector<InventoryItem>> & inventoryItems,const std::shared_ptr<std::map<std::string,std::string>> & ledConnections,Callback && callback,size_t ledConnectionsIndex=0)1293 void getInventoryLedData(
1294     const std::shared_ptr<SensorsAsyncResp>& sensorsAsyncResp,
1295     const std::shared_ptr<std::vector<InventoryItem>>& inventoryItems,
1296     const std::shared_ptr<std::map<std::string, std::string>>& ledConnections,
1297     Callback&& callback, size_t ledConnectionsIndex = 0)
1298 {
1299     BMCWEB_LOG_DEBUG("getInventoryLedData enter");
1300 
1301     // If no more connections left, call callback
1302     if (ledConnectionsIndex >= ledConnections->size())
1303     {
1304         callback();
1305         BMCWEB_LOG_DEBUG("getInventoryLedData exit");
1306         return;
1307     }
1308 
1309     // Get inventory item data from current connection
1310     auto it = ledConnections->begin();
1311     std::advance(it, ledConnectionsIndex);
1312     if (it != ledConnections->end())
1313     {
1314         const std::string& ledPath = (*it).first;
1315         const std::string& ledConnection = (*it).second;
1316         // Response handler for Get State property
1317         auto respHandler =
1318             [sensorsAsyncResp, inventoryItems, ledConnections, ledPath,
1319              callback = std::forward<Callback>(callback),
1320              ledConnectionsIndex](const boost::system::error_code& ec,
1321                                   const std::string& state) mutable {
1322                 BMCWEB_LOG_DEBUG("getInventoryLedData respHandler enter");
1323                 if (ec)
1324                 {
1325                     BMCWEB_LOG_ERROR(
1326                         "getInventoryLedData respHandler DBus error {}", ec);
1327                     messages::internalError(sensorsAsyncResp->asyncResp->res);
1328                     return;
1329                 }
1330 
1331                 BMCWEB_LOG_DEBUG("Led state: {}", state);
1332                 // Find inventory item with this LED object path
1333                 InventoryItem* inventoryItem =
1334                     findInventoryItemForLed(*inventoryItems, ledPath);
1335                 if (inventoryItem != nullptr)
1336                 {
1337                     // Store LED state in InventoryItem
1338                     if (state.ends_with("On"))
1339                     {
1340                         inventoryItem->ledState = sensor_utils::LedState::ON;
1341                     }
1342                     else if (state.ends_with("Blink"))
1343                     {
1344                         inventoryItem->ledState = sensor_utils::LedState::BLINK;
1345                     }
1346                     else if (state.ends_with("Off"))
1347                     {
1348                         inventoryItem->ledState = sensor_utils::LedState::OFF;
1349                     }
1350                     else
1351                     {
1352                         inventoryItem->ledState =
1353                             sensor_utils::LedState::UNKNOWN;
1354                     }
1355                 }
1356 
1357                 // Recurse to get LED data from next connection
1358                 getInventoryLedData(sensorsAsyncResp, inventoryItems,
1359                                     ledConnections, std::move(callback),
1360                                     ledConnectionsIndex + 1);
1361 
1362                 BMCWEB_LOG_DEBUG("getInventoryLedData respHandler exit");
1363             };
1364 
1365         // Get the State property for the current LED
1366         dbus::utility::getProperty<std::string>(
1367             ledConnection, ledPath, "xyz.openbmc_project.Led.Physical", "State",
1368             std::move(respHandler));
1369     }
1370 
1371     BMCWEB_LOG_DEBUG("getInventoryLedData exit");
1372 }
1373 
1374 /**
1375  * @brief Gets LED data for LEDs associated with given inventory items.
1376  *
1377  * Gets the D-Bus connections (services) that provide LED data for the LEDs
1378  * associated with the specified inventory items.  Then gets the LED data from
1379  * each connection and stores it in the inventory item.
1380  *
1381  * This data is later used to provide sensor property values in the JSON
1382  * response.
1383  *
1384  * Finds the LED data asynchronously.  Invokes callback when information has
1385  * been obtained.
1386  *
1387  * The callback must have the following signature:
1388  *   @code
1389  *   callback()
1390  *   @endcode
1391  *
1392  * @param sensorsAsyncResp Pointer to object holding response data.
1393  * @param inventoryItems D-Bus inventory items associated with sensors.
1394  * @param callback Callback to invoke when inventory items have been obtained.
1395  */
1396 template <typename Callback>
getInventoryLeds(const std::shared_ptr<SensorsAsyncResp> & sensorsAsyncResp,const std::shared_ptr<std::vector<InventoryItem>> & inventoryItems,Callback && callback)1397 void getInventoryLeds(
1398     const std::shared_ptr<SensorsAsyncResp>& sensorsAsyncResp,
1399     const std::shared_ptr<std::vector<InventoryItem>>& inventoryItems,
1400     Callback&& callback)
1401 {
1402     BMCWEB_LOG_DEBUG("getInventoryLeds enter");
1403 
1404     const std::string path = "/xyz/openbmc_project";
1405     constexpr std::array<std::string_view, 1> interfaces = {
1406         "xyz.openbmc_project.Led.Physical"};
1407 
1408     // Make call to ObjectMapper to find all inventory items
1409     dbus::utility::getSubTree(
1410         path, 0, interfaces,
1411         [callback = std::forward<Callback>(callback), sensorsAsyncResp,
1412          inventoryItems](
1413             const boost::system::error_code& ec,
1414             const dbus::utility::MapperGetSubTreeResponse& subtree) mutable {
1415             // Response handler for parsing output from GetSubTree
1416             BMCWEB_LOG_DEBUG("getInventoryLeds respHandler enter");
1417             if (ec)
1418             {
1419                 messages::internalError(sensorsAsyncResp->asyncResp->res);
1420                 BMCWEB_LOG_ERROR("getInventoryLeds respHandler DBus error {}",
1421                                  ec);
1422                 return;
1423             }
1424 
1425             // Build map of LED object paths to connections
1426             std::shared_ptr<std::map<std::string, std::string>> ledConnections =
1427                 std::make_shared<std::map<std::string, std::string>>();
1428 
1429             // Loop through objects from GetSubTree
1430             for (const std::pair<std::string,
1431                                  std::vector<std::pair<
1432                                      std::string, std::vector<std::string>>>>&
1433                      object : subtree)
1434             {
1435                 // Check if object path is LED for one of the specified
1436                 // inventory items
1437                 const std::string& ledPath = object.first;
1438                 if (findInventoryItemForLed(*inventoryItems, ledPath) !=
1439                     nullptr)
1440                 {
1441                     // Add mapping from ledPath to connection
1442                     const std::string& connection =
1443                         object.second.begin()->first;
1444                     (*ledConnections)[ledPath] = connection;
1445                     BMCWEB_LOG_DEBUG("Added mapping {} -> {}", ledPath,
1446                                      connection);
1447                 }
1448             }
1449 
1450             getInventoryLedData(sensorsAsyncResp, inventoryItems,
1451                                 ledConnections, std::move(callback));
1452             BMCWEB_LOG_DEBUG("getInventoryLeds respHandler exit");
1453         });
1454     BMCWEB_LOG_DEBUG("getInventoryLeds exit");
1455 }
1456 
1457 /**
1458  * @brief Gets D-Bus data for Power Supply Attributes such as EfficiencyPercent
1459  *
1460  * Uses the specified connections (services) (currently assumes just one) to
1461  * obtain D-Bus data for Power Supply Attributes. Stores the resulting data in
1462  * the inventoryItems vector. Only stores data in Power Supply inventoryItems.
1463  *
1464  * This data is later used to provide sensor property values in the JSON
1465  * response.
1466  *
1467  * Finds the Power Supply Attributes data asynchronously.  Invokes callback
1468  * when data has been obtained.
1469  *
1470  * The callback must have the following signature:
1471  *   @code
1472  *   callback(std::shared_ptr<std::vector<InventoryItem>> inventoryItems)
1473  *   @endcode
1474  *
1475  * @param sensorsAsyncResp Pointer to object holding response data.
1476  * @param inventoryItems D-Bus inventory items associated with sensors.
1477  * @param psAttributesConnections Connections that provide data for the Power
1478  *        Supply Attributes
1479  * @param callback Callback to invoke when data has been obtained.
1480  */
1481 template <typename Callback>
getPowerSupplyAttributesData(const std::shared_ptr<SensorsAsyncResp> & sensorsAsyncResp,const std::shared_ptr<std::vector<InventoryItem>> & inventoryItems,const std::map<std::string,std::string> & psAttributesConnections,Callback && callback)1482 void getPowerSupplyAttributesData(
1483     const std::shared_ptr<SensorsAsyncResp>& sensorsAsyncResp,
1484     const std::shared_ptr<std::vector<InventoryItem>>& inventoryItems,
1485     const std::map<std::string, std::string>& psAttributesConnections,
1486     Callback&& callback)
1487 {
1488     BMCWEB_LOG_DEBUG("getPowerSupplyAttributesData enter");
1489 
1490     if (psAttributesConnections.empty())
1491     {
1492         BMCWEB_LOG_DEBUG("Can't find PowerSupplyAttributes, no connections!");
1493         callback(inventoryItems);
1494         return;
1495     }
1496 
1497     // Assuming just one connection (service) for now
1498     auto it = psAttributesConnections.begin();
1499 
1500     const std::string& psAttributesPath = (*it).first;
1501     const std::string& psAttributesConnection = (*it).second;
1502 
1503     // Response handler for Get DeratingFactor property
1504     auto respHandler = [sensorsAsyncResp, inventoryItems,
1505                         callback = std::forward<Callback>(callback)](
1506                            const boost::system::error_code& ec,
1507                            uint32_t value) mutable {
1508         BMCWEB_LOG_DEBUG("getPowerSupplyAttributesData respHandler enter");
1509         if (ec)
1510         {
1511             BMCWEB_LOG_ERROR(
1512                 "getPowerSupplyAttributesData respHandler DBus error {}", ec);
1513             messages::internalError(sensorsAsyncResp->asyncResp->res);
1514             return;
1515         }
1516 
1517         BMCWEB_LOG_DEBUG("PS EfficiencyPercent value: {}", value);
1518         // Store value in Power Supply Inventory Items
1519         for (InventoryItem& inventoryItem : *inventoryItems)
1520         {
1521             if (inventoryItem.isPowerSupply)
1522             {
1523                 inventoryItem.powerSupplyEfficiencyPercent =
1524                     static_cast<int>(value);
1525             }
1526         }
1527 
1528         BMCWEB_LOG_DEBUG("getPowerSupplyAttributesData respHandler exit");
1529         callback(inventoryItems);
1530     };
1531 
1532     // Get the DeratingFactor property for the PowerSupplyAttributes
1533     // Currently only property on the interface/only one we care about
1534     dbus::utility::getProperty<uint32_t>(
1535         psAttributesConnection, psAttributesPath,
1536         "xyz.openbmc_project.Control.PowerSupplyAttributes", "DeratingFactor",
1537         std::move(respHandler));
1538 
1539     BMCWEB_LOG_DEBUG("getPowerSupplyAttributesData exit");
1540 }
1541 
1542 /**
1543  * @brief Gets the Power Supply Attributes such as EfficiencyPercent
1544  *
1545  * Gets the D-Bus connection (service) that provides Power Supply Attributes
1546  * data. Then gets the Power Supply Attributes data from the connection
1547  * (currently just assumes 1 connection) and stores the data in the inventory
1548  * item.
1549  *
1550  * This data is later used to provide sensor property values in the JSON
1551  * response. DeratingFactor on D-Bus is mapped to EfficiencyPercent on Redfish.
1552  *
1553  * Finds the Power Supply Attributes data asynchronously. Invokes callback
1554  * when information has been obtained.
1555  *
1556  * The callback must have the following signature:
1557  *   @code
1558  *   callback(std::shared_ptr<std::vector<InventoryItem>> inventoryItems)
1559  *   @endcode
1560  *
1561  * @param sensorsAsyncResp Pointer to object holding response data.
1562  * @param inventoryItems D-Bus inventory items associated with sensors.
1563  * @param callback Callback to invoke when data has been obtained.
1564  */
1565 template <typename Callback>
getPowerSupplyAttributes(const std::shared_ptr<SensorsAsyncResp> & sensorsAsyncResp,const std::shared_ptr<std::vector<InventoryItem>> & inventoryItems,Callback && callback)1566 void getPowerSupplyAttributes(
1567     const std::shared_ptr<SensorsAsyncResp>& sensorsAsyncResp,
1568     const std::shared_ptr<std::vector<InventoryItem>>& inventoryItems,
1569     Callback&& callback)
1570 {
1571     BMCWEB_LOG_DEBUG("getPowerSupplyAttributes enter");
1572 
1573     // Only need the power supply attributes when the Power Schema
1574     if (sensorsAsyncResp->chassisSubNode != sensors::powerNodeStr)
1575     {
1576         BMCWEB_LOG_DEBUG("getPowerSupplyAttributes exit since not Power");
1577         callback(inventoryItems);
1578         return;
1579     }
1580 
1581     constexpr std::array<std::string_view, 1> interfaces = {
1582         "xyz.openbmc_project.Control.PowerSupplyAttributes"};
1583 
1584     // Make call to ObjectMapper to find the PowerSupplyAttributes service
1585     dbus::utility::getSubTree(
1586         "/xyz/openbmc_project", 0, interfaces,
1587         [callback = std::forward<Callback>(callback), sensorsAsyncResp,
1588          inventoryItems](
1589             const boost::system::error_code& ec,
1590             const dbus::utility::MapperGetSubTreeResponse& subtree) mutable {
1591             // Response handler for parsing output from GetSubTree
1592             BMCWEB_LOG_DEBUG("getPowerSupplyAttributes respHandler enter");
1593             if (ec)
1594             {
1595                 messages::internalError(sensorsAsyncResp->asyncResp->res);
1596                 BMCWEB_LOG_ERROR(
1597                     "getPowerSupplyAttributes respHandler DBus error {}", ec);
1598                 return;
1599             }
1600             if (subtree.empty())
1601             {
1602                 BMCWEB_LOG_DEBUG("Can't find Power Supply Attributes!");
1603                 callback(inventoryItems);
1604                 return;
1605             }
1606 
1607             // Currently we only support 1 power supply attribute, use this for
1608             // all the power supplies. Build map of object path to connection.
1609             // Assume just 1 connection and 1 path for now.
1610             std::map<std::string, std::string> psAttributesConnections;
1611 
1612             if (subtree[0].first.empty() || subtree[0].second.empty())
1613             {
1614                 BMCWEB_LOG_DEBUG("Power Supply Attributes mapper error!");
1615                 callback(inventoryItems);
1616                 return;
1617             }
1618 
1619             const std::string& psAttributesPath = subtree[0].first;
1620             const std::string& connection = subtree[0].second.begin()->first;
1621 
1622             if (connection.empty())
1623             {
1624                 BMCWEB_LOG_DEBUG("Power Supply Attributes mapper error!");
1625                 callback(inventoryItems);
1626                 return;
1627             }
1628 
1629             psAttributesConnections[psAttributesPath] = connection;
1630             BMCWEB_LOG_DEBUG("Added mapping {} -> {}", psAttributesPath,
1631                              connection);
1632 
1633             getPowerSupplyAttributesData(sensorsAsyncResp, inventoryItems,
1634                                          psAttributesConnections,
1635                                          std::move(callback));
1636             BMCWEB_LOG_DEBUG("getPowerSupplyAttributes respHandler exit");
1637         });
1638     BMCWEB_LOG_DEBUG("getPowerSupplyAttributes exit");
1639 }
1640 
1641 /**
1642  * @brief Gets inventory items associated with sensors.
1643  *
1644  * Finds the inventory items that are associated with the specified sensors.
1645  * Then gets D-Bus data for the inventory items, such as presence and VPD.
1646  *
1647  * This data is later used to provide sensor property values in the JSON
1648  * response.
1649  *
1650  * Finds the inventory items asynchronously.  Invokes callback when the
1651  * inventory items have been obtained.
1652  *
1653  * The callback must have the following signature:
1654  *   @code
1655  *   callback(std::shared_ptr<std::vector<InventoryItem>> inventoryItems)
1656  *   @endcode
1657  *
1658  * @param sensorsAsyncResp Pointer to object holding response data.
1659  * @param sensorNames All sensors within the current chassis.
1660  * implements ObjectManager.
1661  * @param callback Callback to invoke when inventory items have been obtained.
1662  */
1663 template <typename Callback>
1664 inline void
getInventoryItems(const std::shared_ptr<SensorsAsyncResp> & sensorsAsyncResp,const std::shared_ptr<std::set<std::string>> & sensorNames,Callback && callback)1665     getInventoryItems(const std::shared_ptr<SensorsAsyncResp>& sensorsAsyncResp,
1666                       const std::shared_ptr<std::set<std::string>>& sensorNames,
1667                       Callback&& callback)
1668 {
1669     BMCWEB_LOG_DEBUG("getInventoryItems enter");
1670     auto getInventoryItemAssociationsCb =
1671         [sensorsAsyncResp, callback = std::forward<Callback>(callback)](
1672             const std::shared_ptr<std::vector<InventoryItem>>&
1673                 inventoryItems) mutable {
1674             BMCWEB_LOG_DEBUG("getInventoryItemAssociationsCb enter");
1675             auto getInventoryItemsConnectionsCb =
1676                 [sensorsAsyncResp, inventoryItems,
1677                  callback = std::forward<Callback>(callback)](
1678                     const std::shared_ptr<std::set<std::string>>&
1679                         invConnections) mutable {
1680                     BMCWEB_LOG_DEBUG("getInventoryItemsConnectionsCb enter");
1681                     auto getInventoryItemsDataCb =
1682                         [sensorsAsyncResp, inventoryItems,
1683                          callback =
1684                              std::forward<Callback>(callback)]() mutable {
1685                             BMCWEB_LOG_DEBUG("getInventoryItemsDataCb enter");
1686 
1687                             auto getInventoryLedsCb =
1688                                 [sensorsAsyncResp, inventoryItems,
1689                                  callback = std::forward<Callback>(
1690                                      callback)]() mutable {
1691                                     BMCWEB_LOG_DEBUG(
1692                                         "getInventoryLedsCb enter");
1693                                     // Find Power Supply Attributes and get the
1694                                     // data
1695                                     getPowerSupplyAttributes(
1696                                         sensorsAsyncResp, inventoryItems,
1697                                         std::move(callback));
1698                                     BMCWEB_LOG_DEBUG("getInventoryLedsCb exit");
1699                                 };
1700 
1701                             // Find led connections and get the data
1702                             getInventoryLeds(sensorsAsyncResp, inventoryItems,
1703                                              std::move(getInventoryLedsCb));
1704                             BMCWEB_LOG_DEBUG("getInventoryItemsDataCb exit");
1705                         };
1706 
1707                     // Get inventory item data from connections
1708                     getInventoryItemsData(sensorsAsyncResp, inventoryItems,
1709                                           invConnections,
1710                                           std::move(getInventoryItemsDataCb));
1711                     BMCWEB_LOG_DEBUG("getInventoryItemsConnectionsCb exit");
1712                 };
1713 
1714             // Get connections that provide inventory item data
1715             getInventoryItemsConnections(
1716                 sensorsAsyncResp, inventoryItems,
1717                 std::move(getInventoryItemsConnectionsCb));
1718             BMCWEB_LOG_DEBUG("getInventoryItemAssociationsCb exit");
1719         };
1720 
1721     // Get associations from sensors to inventory items
1722     getInventoryItemAssociations(sensorsAsyncResp, sensorNames,
1723                                  std::move(getInventoryItemAssociationsCb));
1724     BMCWEB_LOG_DEBUG("getInventoryItems exit");
1725 }
1726 
1727 /**
1728  * @brief Returns JSON PowerSupply object for the specified inventory item.
1729  *
1730  * Searches for a JSON PowerSupply object that matches the specified inventory
1731  * item.  If one is not found, a new PowerSupply object is added to the JSON
1732  * array.
1733  *
1734  * Multiple sensors are often associated with one power supply inventory item.
1735  * As a result, multiple sensor values are stored in one JSON PowerSupply
1736  * object.
1737  *
1738  * @param powerSupplyArray JSON array containing Redfish PowerSupply objects.
1739  * @param inventoryItem Inventory item for the power supply.
1740  * @param chassisId Chassis that contains the power supply.
1741  * @return JSON PowerSupply object for the specified inventory item.
1742  */
getPowerSupply(nlohmann::json & powerSupplyArray,const InventoryItem & inventoryItem,const std::string & chassisId)1743 inline nlohmann::json& getPowerSupply(nlohmann::json& powerSupplyArray,
1744                                       const InventoryItem& inventoryItem,
1745                                       const std::string& chassisId)
1746 {
1747     std::string nameS;
1748     nameS.resize(inventoryItem.name.size());
1749     std::ranges::replace_copy(inventoryItem.name, nameS.begin(), '_', ' ');
1750     // Check if matching PowerSupply object already exists in JSON array
1751     for (nlohmann::json& powerSupply : powerSupplyArray)
1752     {
1753         nlohmann::json::iterator nameIt = powerSupply.find("Name");
1754         if (nameIt == powerSupply.end())
1755         {
1756             continue;
1757         }
1758         const std::string* name = nameIt->get_ptr<std::string*>();
1759         if (name == nullptr)
1760         {
1761             continue;
1762         }
1763         if (nameS == *name)
1764         {
1765             return powerSupply;
1766         }
1767     }
1768 
1769     // Add new PowerSupply object to JSON array
1770     powerSupplyArray.push_back({});
1771     nlohmann::json& powerSupply = powerSupplyArray.back();
1772     boost::urls::url url =
1773         boost::urls::format("/redfish/v1/Chassis/{}/Power", chassisId);
1774     url.set_fragment(("/PowerSupplies"_json_pointer).to_string());
1775     powerSupply["@odata.id"] = std::move(url);
1776     std::string escaped;
1777     escaped.resize(inventoryItem.name.size());
1778     std::ranges::replace_copy(inventoryItem.name, escaped.begin(), '_', ' ');
1779     powerSupply["Name"] = std::move(escaped);
1780     powerSupply["Manufacturer"] = inventoryItem.manufacturer;
1781     powerSupply["Model"] = inventoryItem.model;
1782     powerSupply["PartNumber"] = inventoryItem.partNumber;
1783     powerSupply["SerialNumber"] = inventoryItem.serialNumber;
1784     sensor_utils::setLedState(powerSupply, &inventoryItem);
1785 
1786     if (inventoryItem.powerSupplyEfficiencyPercent >= 0)
1787     {
1788         powerSupply["EfficiencyPercent"] =
1789             inventoryItem.powerSupplyEfficiencyPercent;
1790     }
1791 
1792     powerSupply["Status"]["State"] =
1793         sensor_utils::getState(&inventoryItem, true);
1794     const char* health = inventoryItem.isFunctional ? "OK" : "Critical";
1795     powerSupply["Status"]["Health"] = health;
1796 
1797     return powerSupply;
1798 }
1799 
1800 /**
1801  * @brief Gets the values of the specified sensors.
1802  *
1803  * Stores the results as JSON in the SensorsAsyncResp.
1804  *
1805  * Gets the sensor values asynchronously.  Stores the results later when the
1806  * information has been obtained.
1807  *
1808  * The sensorNames set contains all requested sensors for the current chassis.
1809  *
1810  * To minimize the number of DBus calls, the DBus method
1811  * org.freedesktop.DBus.ObjectManager.GetManagedObjects() is used to get the
1812  * values of all sensors provided by a connection (service).
1813  *
1814  * The connections set contains all the connections that provide sensor values.
1815  *
1816  * The InventoryItem vector contains D-Bus inventory items associated with the
1817  * sensors.  Inventory item data is needed for some Redfish sensor properties.
1818  *
1819  * @param SensorsAsyncResp Pointer to object holding response data.
1820  * @param sensorNames All requested sensors within the current chassis.
1821  * @param connections Connections that provide sensor values.
1822  * implements ObjectManager.
1823  * @param inventoryItems Inventory items associated with the sensors.
1824  */
getSensorData(const std::shared_ptr<SensorsAsyncResp> & sensorsAsyncResp,const std::shared_ptr<std::set<std::string>> & sensorNames,const std::set<std::string> & connections,const std::shared_ptr<std::vector<InventoryItem>> & inventoryItems)1825 inline void getSensorData(
1826     const std::shared_ptr<SensorsAsyncResp>& sensorsAsyncResp,
1827     const std::shared_ptr<std::set<std::string>>& sensorNames,
1828     const std::set<std::string>& connections,
1829     const std::shared_ptr<std::vector<InventoryItem>>& inventoryItems)
1830 {
1831     BMCWEB_LOG_DEBUG("getSensorData enter");
1832     // Get managed objects from all services exposing sensors
1833     for (const std::string& connection : connections)
1834     {
1835         sdbusplus::message::object_path sensorPath(
1836             "/xyz/openbmc_project/sensors");
1837         dbus::utility::getManagedObjects(
1838             connection, sensorPath,
1839             [sensorsAsyncResp, sensorNames,
1840              inventoryItems](const boost::system::error_code& ec,
1841                              const dbus::utility::ManagedObjectType& resp) {
1842                 BMCWEB_LOG_DEBUG("getManagedObjectsCb enter");
1843                 if (ec)
1844                 {
1845                     BMCWEB_LOG_ERROR("getManagedObjectsCb DBUS error: {}", ec);
1846                     messages::internalError(sensorsAsyncResp->asyncResp->res);
1847                     return;
1848                 }
1849                 auto chassisSubNode = sensor_utils::chassisSubNodeFromString(
1850                     sensorsAsyncResp->chassisSubNode);
1851                 // Go through all objects and update response with sensor data
1852                 for (const auto& objDictEntry : resp)
1853                 {
1854                     const std::string& objPath =
1855                         static_cast<const std::string&>(objDictEntry.first);
1856                     BMCWEB_LOG_DEBUG("getManagedObjectsCb parsing object {}",
1857                                      objPath);
1858 
1859                     std::vector<std::string> split;
1860                     // Reserve space for
1861                     // /xyz/openbmc_project/sensors/<name>/<subname>
1862                     split.reserve(6);
1863                     // NOLINTNEXTLINE
1864                     bmcweb::split(split, objPath, '/');
1865                     if (split.size() < 6)
1866                     {
1867                         BMCWEB_LOG_ERROR("Got path that isn't long enough {}",
1868                                          objPath);
1869                         continue;
1870                     }
1871                     // These indexes aren't intuitive, as split puts an empty
1872                     // string at the beginning
1873                     const std::string& sensorType = split[4];
1874                     const std::string& sensorName = split[5];
1875                     BMCWEB_LOG_DEBUG("sensorName {} sensorType {}", sensorName,
1876                                      sensorType);
1877                     if (sensorNames->find(objPath) == sensorNames->end())
1878                     {
1879                         BMCWEB_LOG_DEBUG("{} not in sensor list ", sensorName);
1880                         continue;
1881                     }
1882 
1883                     // Find inventory item (if any) associated with sensor
1884                     InventoryItem* inventoryItem =
1885                         findInventoryItemForSensor(inventoryItems, objPath);
1886 
1887                     const std::string& sensorSchema =
1888                         sensorsAsyncResp->chassisSubNode;
1889 
1890                     nlohmann::json* sensorJson = nullptr;
1891 
1892                     if (sensorSchema == sensors::sensorsNodeStr &&
1893                         !sensorsAsyncResp->efficientExpand)
1894                     {
1895                         std::string sensorId =
1896                             redfish::sensor_utils::getSensorId(sensorName,
1897                                                                sensorType);
1898 
1899                         sensorsAsyncResp->asyncResp->res
1900                             .jsonValue["@odata.id"] = boost::urls::format(
1901                             "/redfish/v1/Chassis/{}/{}/{}",
1902                             sensorsAsyncResp->chassisId,
1903                             sensorsAsyncResp->chassisSubNode, sensorId);
1904                         sensorJson =
1905                             &(sensorsAsyncResp->asyncResp->res.jsonValue);
1906                     }
1907                     else
1908                     {
1909                         std::string fieldName;
1910                         if (sensorsAsyncResp->efficientExpand)
1911                         {
1912                             fieldName = "Members";
1913                         }
1914                         else if (sensorType == "temperature")
1915                         {
1916                             fieldName = "Temperatures";
1917                         }
1918                         else if (sensorType == "fan" ||
1919                                  sensorType == "fan_tach" ||
1920                                  sensorType == "fan_pwm")
1921                         {
1922                             fieldName = "Fans";
1923                         }
1924                         else if (sensorType == "voltage")
1925                         {
1926                             fieldName = "Voltages";
1927                         }
1928                         else if (sensorType == "power")
1929                         {
1930                             if (sensorName == "total_power")
1931                             {
1932                                 fieldName = "PowerControl";
1933                             }
1934                             else if ((inventoryItem != nullptr) &&
1935                                      (inventoryItem->isPowerSupply))
1936                             {
1937                                 fieldName = "PowerSupplies";
1938                             }
1939                             else
1940                             {
1941                                 // Other power sensors are in SensorCollection
1942                                 continue;
1943                             }
1944                         }
1945                         else
1946                         {
1947                             BMCWEB_LOG_ERROR(
1948                                 "Unsure how to handle sensorType {}",
1949                                 sensorType);
1950                             continue;
1951                         }
1952 
1953                         nlohmann::json& tempArray =
1954                             sensorsAsyncResp->asyncResp->res
1955                                 .jsonValue[fieldName];
1956                         if (fieldName == "PowerControl")
1957                         {
1958                             if (tempArray.empty())
1959                             {
1960                                 // Put multiple "sensors" into a single
1961                                 // PowerControl. Follows MemberId naming and
1962                                 // naming in power.hpp.
1963                                 nlohmann::json::object_t power;
1964                                 boost::urls::url url = boost::urls::format(
1965                                     "/redfish/v1/Chassis/{}/{}",
1966                                     sensorsAsyncResp->chassisId,
1967                                     sensorsAsyncResp->chassisSubNode);
1968                                 url.set_fragment(
1969                                     (""_json_pointer / fieldName / "0")
1970                                         .to_string());
1971                                 power["@odata.id"] = std::move(url);
1972                                 tempArray.emplace_back(std::move(power));
1973                             }
1974                             sensorJson = &(tempArray.back());
1975                         }
1976                         else if (fieldName == "PowerSupplies")
1977                         {
1978                             if (inventoryItem != nullptr)
1979                             {
1980                                 sensorJson = &(getPowerSupply(
1981                                     tempArray, *inventoryItem,
1982                                     sensorsAsyncResp->chassisId));
1983                             }
1984                         }
1985                         else if (fieldName == "Members")
1986                         {
1987                             std::string sensorId =
1988                                 redfish::sensor_utils::getSensorId(sensorName,
1989                                                                    sensorType);
1990 
1991                             nlohmann::json::object_t member;
1992                             member["@odata.id"] = boost::urls::format(
1993                                 "/redfish/v1/Chassis/{}/{}/{}",
1994                                 sensorsAsyncResp->chassisId,
1995                                 sensorsAsyncResp->chassisSubNode, sensorId);
1996                             tempArray.emplace_back(std::move(member));
1997                             sensorJson = &(tempArray.back());
1998                         }
1999                         else
2000                         {
2001                             nlohmann::json::object_t member;
2002                             boost::urls::url url = boost::urls::format(
2003                                 "/redfish/v1/Chassis/{}/{}",
2004                                 sensorsAsyncResp->chassisId,
2005                                 sensorsAsyncResp->chassisSubNode);
2006                             url.set_fragment(
2007                                 (""_json_pointer / fieldName).to_string());
2008                             member["@odata.id"] = std::move(url);
2009                             tempArray.emplace_back(std::move(member));
2010                             sensorJson = &(tempArray.back());
2011                         }
2012                     }
2013 
2014                     if (sensorJson != nullptr)
2015                     {
2016                         objectInterfacesToJson(
2017                             sensorName, sensorType, chassisSubNode,
2018                             objDictEntry.second, *sensorJson, inventoryItem);
2019 
2020                         std::string path = "/xyz/openbmc_project/sensors/";
2021                         path += sensorType;
2022                         path += "/";
2023                         path += sensorName;
2024                         sensorsAsyncResp->addMetadata(*sensorJson, path);
2025                     }
2026                 }
2027                 if (sensorsAsyncResp.use_count() == 1)
2028                 {
2029                     sortJSONResponse(sensorsAsyncResp);
2030                     if (chassisSubNode ==
2031                             sensor_utils::ChassisSubNode::sensorsNode &&
2032                         sensorsAsyncResp->efficientExpand)
2033                     {
2034                         sensorsAsyncResp->asyncResp->res
2035                             .jsonValue["Members@odata.count"] =
2036                             sensorsAsyncResp->asyncResp->res
2037                                 .jsonValue["Members"]
2038                                 .size();
2039                     }
2040                     else if (chassisSubNode ==
2041                              sensor_utils::ChassisSubNode::thermalNode)
2042                     {
2043                         populateFanRedundancy(sensorsAsyncResp);
2044                     }
2045                 }
2046                 BMCWEB_LOG_DEBUG("getManagedObjectsCb exit");
2047             });
2048     }
2049     BMCWEB_LOG_DEBUG("getSensorData exit");
2050 }
2051 
2052 inline void
processSensorList(const std::shared_ptr<SensorsAsyncResp> & sensorsAsyncResp,const std::shared_ptr<std::set<std::string>> & sensorNames)2053     processSensorList(const std::shared_ptr<SensorsAsyncResp>& sensorsAsyncResp,
2054                       const std::shared_ptr<std::set<std::string>>& sensorNames)
2055 {
2056     auto getConnectionCb = [sensorsAsyncResp, sensorNames](
2057                                const std::set<std::string>& connections) {
2058         BMCWEB_LOG_DEBUG("getConnectionCb enter");
2059         auto getInventoryItemsCb =
2060             [sensorsAsyncResp, sensorNames, connections](
2061                 const std::shared_ptr<std::vector<InventoryItem>>&
2062                     inventoryItems) mutable {
2063                 BMCWEB_LOG_DEBUG("getInventoryItemsCb enter");
2064                 // Get sensor data and store results in JSON
2065                 getSensorData(sensorsAsyncResp, sensorNames, connections,
2066                               inventoryItems);
2067                 BMCWEB_LOG_DEBUG("getInventoryItemsCb exit");
2068             };
2069 
2070         // Get inventory items associated with sensors
2071         getInventoryItems(sensorsAsyncResp, sensorNames,
2072                           std::move(getInventoryItemsCb));
2073 
2074         BMCWEB_LOG_DEBUG("getConnectionCb exit");
2075     };
2076 
2077     // Get set of connections that provide sensor values
2078     getConnections(sensorsAsyncResp, sensorNames, std::move(getConnectionCb));
2079 }
2080 
2081 /**
2082  * @brief Entry point for retrieving sensors data related to requested
2083  *        chassis.
2084  * @param SensorsAsyncResp   Pointer to object holding response data
2085  */
2086 inline void
getChassisData(const std::shared_ptr<SensorsAsyncResp> & sensorsAsyncResp)2087     getChassisData(const std::shared_ptr<SensorsAsyncResp>& sensorsAsyncResp)
2088 {
2089     BMCWEB_LOG_DEBUG("getChassisData enter");
2090     auto getChassisCb =
2091         [sensorsAsyncResp](
2092             const std::shared_ptr<std::set<std::string>>& sensorNames) {
2093             BMCWEB_LOG_DEBUG("getChassisCb enter");
2094             processSensorList(sensorsAsyncResp, sensorNames);
2095             BMCWEB_LOG_DEBUG("getChassisCb exit");
2096         };
2097     // SensorCollection doesn't contain the Redundancy property
2098     if (sensorsAsyncResp->chassisSubNode != sensors::sensorsNodeStr)
2099     {
2100         sensorsAsyncResp->asyncResp->res.jsonValue["Redundancy"] =
2101             nlohmann::json::array();
2102     }
2103     // Get set of sensors in chassis
2104     getChassis(sensorsAsyncResp->asyncResp, sensorsAsyncResp->chassisId,
2105                sensorsAsyncResp->chassisSubNode, sensorsAsyncResp->types,
2106                std::move(getChassisCb));
2107     BMCWEB_LOG_DEBUG("getChassisData exit");
2108 }
2109 
2110 /**
2111  * @brief Find the requested sensorName in the list of all sensors supplied by
2112  * the chassis node
2113  *
2114  * @param sensorName   The sensor name supplied in the PATCH request
2115  * @param sensorsList  The list of sensors managed by the chassis node
2116  * @param sensorsModified  The list of sensors that were found as a result of
2117  *                         repeated calls to this function
2118  */
findSensorNameUsingSensorPath(std::string_view sensorName,const std::set<std::string> & sensorsList,std::set<std::string> & sensorsModified)2119 inline bool findSensorNameUsingSensorPath(
2120     std::string_view sensorName, const std::set<std::string>& sensorsList,
2121     std::set<std::string>& sensorsModified)
2122 {
2123     for (const auto& chassisSensor : sensorsList)
2124     {
2125         sdbusplus::message::object_path path(chassisSensor);
2126         std::string thisSensorName = path.filename();
2127         if (thisSensorName.empty())
2128         {
2129             continue;
2130         }
2131         if (thisSensorName == sensorName)
2132         {
2133             sensorsModified.emplace(chassisSensor);
2134             return true;
2135         }
2136     }
2137     return false;
2138 }
2139 
2140 /**
2141  * @brief Entry point for overriding sensor values of given sensor
2142  *
2143  * @param sensorAsyncResp   response object
2144  * @param allCollections   Collections extract from sensors' request patch info
2145  * @param chassisSubNode   Chassis Node for which the query has to happen
2146  */
setSensorsOverride(const std::shared_ptr<SensorsAsyncResp> & sensorAsyncResp,std::unordered_map<std::string,std::vector<nlohmann::json::object_t>> & allCollections)2147 inline void setSensorsOverride(
2148     const std::shared_ptr<SensorsAsyncResp>& sensorAsyncResp,
2149     std::unordered_map<std::string, std::vector<nlohmann::json::object_t>>&
2150         allCollections)
2151 {
2152     BMCWEB_LOG_INFO("setSensorsOverride for subNode{}",
2153                     sensorAsyncResp->chassisSubNode);
2154 
2155     std::string_view propertyValueName;
2156     std::unordered_map<std::string, std::pair<double, std::string>> overrideMap;
2157     std::string memberId;
2158     double value = 0.0;
2159     for (auto& collectionItems : allCollections)
2160     {
2161         if (collectionItems.first == "Temperatures")
2162         {
2163             propertyValueName = "ReadingCelsius";
2164         }
2165         else if (collectionItems.first == "Fans")
2166         {
2167             propertyValueName = "Reading";
2168         }
2169         else
2170         {
2171             propertyValueName = "ReadingVolts";
2172         }
2173         for (auto& item : collectionItems.second)
2174         {
2175             if (!json_util::readJsonObject( //
2176                     item, sensorAsyncResp->asyncResp->res, //
2177                     "MemberId", memberId, //
2178                     propertyValueName, value //
2179                     ))
2180             {
2181                 return;
2182             }
2183             overrideMap.emplace(memberId,
2184                                 std::make_pair(value, collectionItems.first));
2185         }
2186     }
2187 
2188     auto getChassisSensorListCb = [sensorAsyncResp, overrideMap,
2189                                    propertyValueNameStr =
2190                                        std::string(propertyValueName)](
2191                                       const std::shared_ptr<
2192                                           std::set<std::string>>& sensorsList) {
2193         // Match sensor names in the PATCH request to those managed by the
2194         // chassis node
2195         const std::shared_ptr<std::set<std::string>> sensorNames =
2196             std::make_shared<std::set<std::string>>();
2197         for (const auto& item : overrideMap)
2198         {
2199             const auto& sensor = item.first;
2200             std::pair<std::string, std::string> sensorNameType =
2201                 redfish::sensor_utils::splitSensorNameAndType(sensor);
2202             if (!findSensorNameUsingSensorPath(sensorNameType.second,
2203                                                *sensorsList, *sensorNames))
2204             {
2205                 BMCWEB_LOG_INFO("Unable to find memberId {}", item.first);
2206                 messages::resourceNotFound(sensorAsyncResp->asyncResp->res,
2207                                            item.second.second, item.first);
2208                 return;
2209             }
2210         }
2211         // Get the connection to which the memberId belongs
2212         auto getObjectsWithConnectionCb = [sensorAsyncResp, overrideMap,
2213                                            propertyValueNameStr](
2214                                               const std::set<
2215                                                   std::string>& /*connections*/,
2216                                               const std::set<std::pair<
2217                                                   std::string, std::string>>&
2218                                                   objectsWithConnection) {
2219             if (objectsWithConnection.size() != overrideMap.size())
2220             {
2221                 BMCWEB_LOG_INFO(
2222                     "Unable to find all objects with proper connection {} requested {}",
2223                     objectsWithConnection.size(), overrideMap.size());
2224                 messages::resourceNotFound(
2225                     sensorAsyncResp->asyncResp->res,
2226                     sensorAsyncResp->chassisSubNode == sensors::thermalNodeStr
2227                         ? "Temperatures"
2228                         : "Voltages",
2229                     "Count");
2230                 return;
2231             }
2232             for (const auto& item : objectsWithConnection)
2233             {
2234                 sdbusplus::message::object_path path(item.first);
2235                 std::string sensorName = path.filename();
2236                 if (sensorName.empty())
2237                 {
2238                     messages::internalError(sensorAsyncResp->asyncResp->res);
2239                     return;
2240                 }
2241                 std::string id = redfish::sensor_utils::getSensorId(
2242                     sensorName, path.parent_path().filename());
2243 
2244                 const auto& iterator = overrideMap.find(id);
2245                 if (iterator == overrideMap.end())
2246                 {
2247                     BMCWEB_LOG_INFO("Unable to find sensor object{}",
2248                                     item.first);
2249                     messages::internalError(sensorAsyncResp->asyncResp->res);
2250                     return;
2251                 }
2252                 setDbusProperty(sensorAsyncResp->asyncResp,
2253                                 propertyValueNameStr, item.second, item.first,
2254                                 "xyz.openbmc_project.Sensor.Value", "Value",
2255                                 iterator->second.first);
2256             }
2257         };
2258         // Get object with connection for the given sensor name
2259         getObjectsWithConnection(sensorAsyncResp, sensorNames,
2260                                  std::move(getObjectsWithConnectionCb));
2261     };
2262     // get full sensor list for the given chassisId and cross verify the sensor.
2263     getChassis(sensorAsyncResp->asyncResp, sensorAsyncResp->chassisId,
2264                sensorAsyncResp->chassisSubNode, sensorAsyncResp->types,
2265                std::move(getChassisSensorListCb));
2266 }
2267 
2268 /**
2269  * @brief Retrieves mapping of Redfish URIs to sensor value property to D-Bus
2270  * path of the sensor.
2271  *
2272  * Function builds valid Redfish response for sensor query of given chassis and
2273  * node. It then builds metadata about Redfish<->D-Bus correlations and provides
2274  * it to caller in a callback.
2275  *
2276  * @param chassis   Chassis for which retrieval should be performed
2277  * @param node  Node (group) of sensors. See sensor_utils::node for supported
2278  * values
2279  * @param mapComplete   Callback to be called with retrieval result
2280  */
2281 template <typename Callback>
retrieveUriToDbusMap(const std::string & chassis,const std::string & node,Callback && mapComplete)2282 inline void retrieveUriToDbusMap(
2283     const std::string& chassis, const std::string& node, Callback&& mapComplete)
2284 {
2285     decltype(sensors::paths)::const_iterator pathIt =
2286         std::find_if(sensors::paths.cbegin(), sensors::paths.cend(),
2287                      [&node](auto&& val) { return val.first == node; });
2288     if (pathIt == sensors::paths.cend())
2289     {
2290         BMCWEB_LOG_ERROR("Wrong node provided : {}", node);
2291         std::map<std::string, std::string> noop;
2292         mapComplete(boost::beast::http::status::bad_request, noop);
2293         return;
2294     }
2295 
2296     auto asyncResp = std::make_shared<bmcweb::AsyncResp>();
2297     auto callback =
2298         [asyncResp, mapCompleteCb = std::forward<Callback>(mapComplete)](
2299             const boost::beast::http::status status,
2300             const std::map<std::string, std::string>& uriToDbus) {
2301             mapCompleteCb(status, uriToDbus);
2302         };
2303 
2304     auto resp = std::make_shared<SensorsAsyncResp>(
2305         asyncResp, chassis, pathIt->second, node, std::move(callback));
2306     getChassisData(resp);
2307 }
2308 
2309 namespace sensors
2310 {
2311 
getChassisCallback(const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,std::string_view chassisId,std::string_view chassisSubNode,const std::shared_ptr<std::set<std::string>> & sensorNames)2312 inline void getChassisCallback(
2313     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2314     std::string_view chassisId, std::string_view chassisSubNode,
2315     const std::shared_ptr<std::set<std::string>>& sensorNames)
2316 {
2317     BMCWEB_LOG_DEBUG("getChassisCallback enter ");
2318 
2319     nlohmann::json& entriesArray = asyncResp->res.jsonValue["Members"];
2320     for (const std::string& sensor : *sensorNames)
2321     {
2322         BMCWEB_LOG_DEBUG("Adding sensor: {}", sensor);
2323 
2324         sdbusplus::message::object_path path(sensor);
2325         std::string sensorName = path.filename();
2326         if (sensorName.empty())
2327         {
2328             BMCWEB_LOG_ERROR("Invalid sensor path: {}", sensor);
2329             messages::internalError(asyncResp->res);
2330             return;
2331         }
2332         std::string type = path.parent_path().filename();
2333         std::string id = redfish::sensor_utils::getSensorId(sensorName, type);
2334 
2335         nlohmann::json::object_t member;
2336         member["@odata.id"] = boost::urls::format(
2337             "/redfish/v1/Chassis/{}/{}/{}", chassisId, chassisSubNode, id);
2338 
2339         entriesArray.emplace_back(std::move(member));
2340     }
2341 
2342     asyncResp->res.jsonValue["Members@odata.count"] = entriesArray.size();
2343     BMCWEB_LOG_DEBUG("getChassisCallback exit");
2344 }
2345 
handleSensorCollectionGet(App & app,const crow::Request & req,const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & chassisId)2346 inline void handleSensorCollectionGet(
2347     App& app, const crow::Request& req,
2348     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2349     const std::string& chassisId)
2350 {
2351     query_param::QueryCapabilities capabilities = {
2352         .canDelegateExpandLevel = 1,
2353     };
2354     query_param::Query delegatedQuery;
2355     if (!redfish::setUpRedfishRouteWithDelegation(app, req, asyncResp,
2356                                                   delegatedQuery, capabilities))
2357     {
2358         return;
2359     }
2360 
2361     if (delegatedQuery.expandType != query_param::ExpandType::None)
2362     {
2363         // we perform efficient expand.
2364         auto sensorsAsyncResp = std::make_shared<SensorsAsyncResp>(
2365             asyncResp, chassisId, sensors::dbus::sensorPaths,
2366             sensors::sensorsNodeStr,
2367             /*efficientExpand=*/true);
2368         getChassisData(sensorsAsyncResp);
2369 
2370         BMCWEB_LOG_DEBUG(
2371             "SensorCollection doGet exit via efficient expand handler");
2372         return;
2373     }
2374 
2375     // We get all sensors as hyperlinkes in the chassis (this
2376     // implies we reply on the default query parameters handler)
2377     getChassis(asyncResp, chassisId, sensors::sensorsNodeStr, dbus::sensorPaths,
2378                std::bind_front(sensors::getChassisCallback, asyncResp,
2379                                chassisId, sensors::sensorsNodeStr));
2380 }
2381 
2382 inline void
getSensorFromDbus(const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & sensorPath,const::dbus::utility::MapperGetObject & mapperResponse)2383     getSensorFromDbus(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2384                       const std::string& sensorPath,
2385                       const ::dbus::utility::MapperGetObject& mapperResponse)
2386 {
2387     if (mapperResponse.size() != 1)
2388     {
2389         messages::internalError(asyncResp->res);
2390         return;
2391     }
2392     const auto& valueIface = *mapperResponse.begin();
2393     const std::string& connectionName = valueIface.first;
2394     BMCWEB_LOG_DEBUG("Looking up {}", connectionName);
2395     BMCWEB_LOG_DEBUG("Path {}", sensorPath);
2396 
2397     ::dbus::utility::getAllProperties(
2398         *crow::connections::systemBus, connectionName, sensorPath, "",
2399         [asyncResp,
2400          sensorPath](const boost::system::error_code& ec,
2401                      const ::dbus::utility::DBusPropertiesMap& valuesDict) {
2402             if (ec)
2403             {
2404                 messages::internalError(asyncResp->res);
2405                 return;
2406             }
2407             sdbusplus::message::object_path path(sensorPath);
2408             std::string name = path.filename();
2409             path = path.parent_path();
2410             std::string type = path.filename();
2411             sensor_utils::objectPropertiesToJson(
2412                 name, type, sensor_utils::ChassisSubNode::sensorsNode,
2413                 valuesDict, asyncResp->res.jsonValue, nullptr);
2414         });
2415 }
2416 
handleSensorGet(App & app,const crow::Request & req,const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & chassisId,const std::string & sensorId)2417 inline void handleSensorGet(App& app, const crow::Request& req,
2418                             const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2419                             const std::string& chassisId,
2420                             const std::string& sensorId)
2421 {
2422     if (!redfish::setUpRedfishRoute(app, req, asyncResp))
2423     {
2424         return;
2425     }
2426     std::pair<std::string, std::string> nameType =
2427         redfish::sensor_utils::splitSensorNameAndType(sensorId);
2428     if (nameType.first.empty() || nameType.second.empty())
2429     {
2430         messages::resourceNotFound(asyncResp->res, sensorId, "Sensor");
2431         return;
2432     }
2433 
2434     asyncResp->res.jsonValue["@odata.id"] = boost::urls::format(
2435         "/redfish/v1/Chassis/{}/Sensors/{}", chassisId, sensorId);
2436 
2437     BMCWEB_LOG_DEBUG("Sensor doGet enter");
2438 
2439     constexpr std::array<std::string_view, 1> interfaces = {
2440         "xyz.openbmc_project.Sensor.Value"};
2441     std::string sensorPath = "/xyz/openbmc_project/sensors/" + nameType.first +
2442                              '/' + nameType.second;
2443     // Get a list of all of the sensors that implement Sensor.Value
2444     // and get the path and service name associated with the sensor
2445     ::dbus::utility::getDbusObject(
2446         sensorPath, interfaces,
2447         [asyncResp, sensorId,
2448          sensorPath](const boost::system::error_code& ec,
2449                      const ::dbus::utility::MapperGetObject& subtree) {
2450             BMCWEB_LOG_DEBUG("respHandler1 enter");
2451             if (ec == boost::system::errc::io_error)
2452             {
2453                 BMCWEB_LOG_WARNING("Sensor not found from getSensorPaths");
2454                 messages::resourceNotFound(asyncResp->res, sensorId, "Sensor");
2455                 return;
2456             }
2457             if (ec)
2458             {
2459                 messages::internalError(asyncResp->res);
2460                 BMCWEB_LOG_ERROR(
2461                     "Sensor getSensorPaths resp_handler: Dbus error {}", ec);
2462                 return;
2463             }
2464             getSensorFromDbus(asyncResp, sensorPath, subtree);
2465             BMCWEB_LOG_DEBUG("respHandler1 exit");
2466         });
2467 }
2468 
2469 } // namespace sensors
2470 
requestRoutesSensorCollection(App & app)2471 inline void requestRoutesSensorCollection(App& app)
2472 {
2473     BMCWEB_ROUTE(app, "/redfish/v1/Chassis/<str>/Sensors/")
2474         .privileges(redfish::privileges::getSensorCollection)
2475         .methods(boost::beast::http::verb::get)(
2476             std::bind_front(sensors::handleSensorCollectionGet, std::ref(app)));
2477 }
2478 
requestRoutesSensor(App & app)2479 inline void requestRoutesSensor(App& app)
2480 {
2481     BMCWEB_ROUTE(app, "/redfish/v1/Chassis/<str>/Sensors/<str>/")
2482         .privileges(redfish::privileges::getSensor)
2483         .methods(boost::beast::http::verb::get)(
2484             std::bind_front(sensors::handleSensorGet, std::ref(app)));
2485 }
2486 
2487 } // namespace redfish
2488