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