xref: /openbmc/bmcweb/features/redfish/lib/sensors.hpp (revision c2bf7f9911c3be7606809e682e00c0389f114c08)
1 /*
2 // Copyright (c) 2018 Intel Corporation
3 //
4 // Licensed under the Apache License, Version 2.0 (the "License");
5 // you may not use this file except in compliance with the License.
6 // You may obtain a copy of the License at
7 //
8 //      http://www.apache.org/licenses/LICENSE-2.0
9 //
10 // Unless required by applicable law or agreed to in writing, software
11 // distributed under the License is distributed on an "AS IS" BASIS,
12 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 // See the License for the specific language governing permissions and
14 // limitations under the License.
15 */
16 #pragma once
17 
18 #include "node.hpp"
19 
20 #include <boost/algorithm/string/predicate.hpp>
21 #include <boost/algorithm/string/split.hpp>
22 #include <boost/container/flat_map.hpp>
23 #include <boost/range/algorithm/replace_copy_if.hpp>
24 #include <dbus_singleton.hpp>
25 #include <utils/json_utils.hpp>
26 
27 #include <cmath>
28 #include <utility>
29 #include <variant>
30 
31 namespace redfish
32 {
33 
34 using GetSubTreeType = std::vector<
35     std::pair<std::string,
36               std::vector<std::pair<std::string, std::vector<std::string>>>>>;
37 
38 using SensorVariant =
39     std::variant<int64_t, double, uint32_t, bool, std::string>;
40 
41 using ManagedObjectsVectorType = std::vector<std::pair<
42     sdbusplus::message::object_path,
43     boost::container::flat_map<
44         std::string, boost::container::flat_map<std::string, SensorVariant>>>>;
45 
46 namespace sensors
47 {
48 namespace node
49 {
50 static constexpr std::string_view power = "Power";
51 static constexpr std::string_view sensors = "Sensors";
52 static constexpr std::string_view thermal = "Thermal";
53 } // namespace node
54 
55 namespace dbus
56 {
57 
58 static const boost::container::flat_map<std::string_view,
59                                         std::vector<const char*>>
60     paths = {{node::power,
61               {"/xyz/openbmc_project/sensors/voltage",
62                "/xyz/openbmc_project/sensors/power"}},
63              {node::sensors,
64               {"/xyz/openbmc_project/sensors/power",
65                "/xyz/openbmc_project/sensors/current",
66                "/xyz/openbmc_project/sensors/utilization"}},
67              {node::thermal,
68               {"/xyz/openbmc_project/sensors/fan_tach",
69                "/xyz/openbmc_project/sensors/temperature",
70                "/xyz/openbmc_project/sensors/fan_pwm"}}};
71 } // namespace dbus
72 
73 inline const char* toReadingType(const std::string& sensorType)
74 {
75     if (sensorType == "voltage")
76     {
77         return "Voltage";
78     }
79     if (sensorType == "power")
80     {
81         return "Power";
82     }
83     if (sensorType == "current")
84     {
85         return "Current";
86     }
87     if (sensorType == "fan_tach")
88     {
89         return "Rotational";
90     }
91     if (sensorType == "temperature")
92     {
93         return "Temperature";
94     }
95     if (sensorType == "fan_pwm" || sensorType == "utilization")
96     {
97         return "Percent";
98     }
99     if (sensorType == "altitude")
100     {
101         return "Altitude";
102     }
103     if (sensorType == "airflow")
104     {
105         return "AirFlow";
106     }
107     if (sensorType == "energy")
108     {
109         return "EnergyJoules";
110     }
111     return "";
112 }
113 
114 inline const char* toReadingUnits(const std::string& sensorType)
115 {
116     if (sensorType == "voltage")
117     {
118         return "V";
119     }
120     if (sensorType == "power")
121     {
122         return "W";
123     }
124     if (sensorType == "current")
125     {
126         return "A";
127     }
128     if (sensorType == "fan_tach")
129     {
130         return "RPM";
131     }
132     if (sensorType == "temperature")
133     {
134         return "Cel";
135     }
136     if (sensorType == "fan_pwm" || sensorType == "utilization")
137     {
138         return "%";
139     }
140     if (sensorType == "altitude")
141     {
142         return "m";
143     }
144     if (sensorType == "airflow")
145     {
146         return "cft_i/min";
147     }
148     if (sensorType == "energy")
149     {
150         return "J";
151     }
152     return "";
153 }
154 } // namespace sensors
155 
156 /**
157  * SensorsAsyncResp
158  * Gathers data needed for response processing after async calls are done
159  */
160 class SensorsAsyncResp
161 {
162   public:
163     using DataCompleteCb = std::function<void(
164         const boost::beast::http::status status,
165         const boost::container::flat_map<std::string, std::string>& uriToDbus)>;
166 
167     struct SensorData
168     {
169         const std::string name;
170         std::string uri;
171         const std::string valueKey;
172         const std::string dbusPath;
173     };
174 
175     SensorsAsyncResp(crow::Response& response, const std::string& chassisIdIn,
176                      const std::vector<const char*>& typesIn,
177                      const std::string_view& subNode) :
178         res(response),
179         chassisId(chassisIdIn), types(typesIn), chassisSubNode(subNode)
180     {}
181 
182     // Store extra data about sensor mapping and return it in callback
183     SensorsAsyncResp(crow::Response& response, const std::string& chassisIdIn,
184                      const std::vector<const char*>& typesIn,
185                      const std::string_view& subNode,
186                      DataCompleteCb&& creationComplete) :
187         res(response),
188         chassisId(chassisIdIn), types(typesIn),
189         chassisSubNode(subNode), metadata{std::vector<SensorData>()},
190         dataComplete{std::move(creationComplete)}
191     {}
192 
193     ~SensorsAsyncResp()
194     {
195         if (res.result() == boost::beast::http::status::internal_server_error)
196         {
197             // Reset the json object to clear out any data that made it in
198             // before the error happened todo(ed) handle error condition with
199             // proper code
200             res.jsonValue = nlohmann::json::object();
201         }
202 
203         if (dataComplete && metadata)
204         {
205             boost::container::flat_map<std::string, std::string> map;
206             if (res.result() == boost::beast::http::status::ok)
207             {
208                 for (auto& sensor : *metadata)
209                 {
210                     map.insert(std::make_pair(sensor.uri + sensor.valueKey,
211                                               sensor.dbusPath));
212                 }
213             }
214             dataComplete(res.result(), map);
215         }
216 
217         res.end();
218     }
219 
220     void addMetadata(const nlohmann::json& sensorObject,
221                      const std::string& valueKey, const std::string& dbusPath)
222     {
223         if (metadata)
224         {
225             metadata->emplace_back(SensorData{sensorObject["Name"],
226                                               sensorObject["@odata.id"],
227                                               valueKey, dbusPath});
228         }
229     }
230 
231     void updateUri(const std::string& name, const std::string& uri)
232     {
233         if (metadata)
234         {
235             for (auto& sensor : *metadata)
236             {
237                 if (sensor.name == name)
238                 {
239                     sensor.uri = uri;
240                 }
241             }
242         }
243     }
244 
245     crow::Response& res;
246     const std::string chassisId;
247     const std::vector<const char*> types;
248     const std::string chassisSubNode;
249 
250   private:
251     std::optional<std::vector<SensorData>> metadata;
252     DataCompleteCb dataComplete;
253 };
254 
255 /**
256  * Possible states for physical inventory leds
257  */
258 enum class LedState
259 {
260     OFF,
261     ON,
262     BLINK,
263     UNKNOWN
264 };
265 
266 /**
267  * D-Bus inventory item associated with one or more sensors.
268  */
269 class InventoryItem
270 {
271   public:
272     InventoryItem(const std::string& objPath) :
273         objectPath(objPath), name(), isPresent(true), isFunctional(true),
274         isPowerSupply(false), powerSupplyEfficiencyPercent(-1), manufacturer(),
275         model(), partNumber(), serialNumber(), sensors(), ledObjectPath(""),
276         ledState(LedState::UNKNOWN)
277     {
278         // Set inventory item name to last node of object path
279         sdbusplus::message::object_path path(objectPath);
280         name = path.filename();
281         if (name.empty())
282         {
283             BMCWEB_LOG_ERROR << "Failed to find '/' in " << objectPath;
284         }
285     }
286 
287     std::string objectPath;
288     std::string name;
289     bool isPresent;
290     bool isFunctional;
291     bool isPowerSupply;
292     int powerSupplyEfficiencyPercent;
293     std::string manufacturer;
294     std::string model;
295     std::string partNumber;
296     std::string serialNumber;
297     std::set<std::string> sensors;
298     std::string ledObjectPath;
299     LedState ledState;
300 };
301 
302 /**
303  * @brief Get objects with connection necessary for sensors
304  * @param SensorsAsyncResp Pointer to object holding response data
305  * @param sensorNames Sensors retrieved from chassis
306  * @param callback Callback for processing gathered connections
307  */
308 template <typename Callback>
309 void getObjectsWithConnection(
310     const std::shared_ptr<SensorsAsyncResp>& sensorsAsyncResp,
311     const std::shared_ptr<boost::container::flat_set<std::string>>& sensorNames,
312     Callback&& callback)
313 {
314     BMCWEB_LOG_DEBUG << "getObjectsWithConnection enter";
315     const std::string path = "/xyz/openbmc_project/sensors";
316     const std::array<std::string, 1> interfaces = {
317         "xyz.openbmc_project.Sensor.Value"};
318 
319     // Response handler for parsing objects subtree
320     auto respHandler = [callback{std::move(callback)}, sensorsAsyncResp,
321                         sensorNames](const boost::system::error_code ec,
322                                      const GetSubTreeType& subtree) {
323         BMCWEB_LOG_DEBUG << "getObjectsWithConnection resp_handler enter";
324         if (ec)
325         {
326             messages::internalError(sensorsAsyncResp->res);
327             BMCWEB_LOG_ERROR
328                 << "getObjectsWithConnection resp_handler: Dbus error " << ec;
329             return;
330         }
331 
332         BMCWEB_LOG_DEBUG << "Found " << subtree.size() << " subtrees";
333 
334         // Make unique list of connections only for requested sensor types and
335         // found in the chassis
336         boost::container::flat_set<std::string> connections;
337         std::set<std::pair<std::string, std::string>> objectsWithConnection;
338         // Intrinsic to avoid malloc.  Most systems will have < 8 sensor
339         // producers
340         connections.reserve(8);
341 
342         BMCWEB_LOG_DEBUG << "sensorNames list count: " << sensorNames->size();
343         for (const std::string& tsensor : *sensorNames)
344         {
345             BMCWEB_LOG_DEBUG << "Sensor to find: " << tsensor;
346         }
347 
348         for (const std::pair<
349                  std::string,
350                  std::vector<std::pair<std::string, std::vector<std::string>>>>&
351                  object : subtree)
352         {
353             if (sensorNames->find(object.first) != sensorNames->end())
354             {
355                 for (const std::pair<std::string, std::vector<std::string>>&
356                          objData : object.second)
357                 {
358                     BMCWEB_LOG_DEBUG << "Adding connection: " << objData.first;
359                     connections.insert(objData.first);
360                     objectsWithConnection.insert(
361                         std::make_pair(object.first, objData.first));
362                 }
363             }
364         }
365         BMCWEB_LOG_DEBUG << "Found " << connections.size() << " connections";
366         callback(std::move(connections), std::move(objectsWithConnection));
367         BMCWEB_LOG_DEBUG << "getObjectsWithConnection resp_handler exit";
368     };
369     // Make call to ObjectMapper to find all sensors objects
370     crow::connections::systemBus->async_method_call(
371         std::move(respHandler), "xyz.openbmc_project.ObjectMapper",
372         "/xyz/openbmc_project/object_mapper",
373         "xyz.openbmc_project.ObjectMapper", "GetSubTree", path, 2, interfaces);
374     BMCWEB_LOG_DEBUG << "getObjectsWithConnection exit";
375 }
376 
377 /**
378  * @brief Create connections necessary for sensors
379  * @param SensorsAsyncResp Pointer to object holding response data
380  * @param sensorNames Sensors retrieved from chassis
381  * @param callback Callback for processing gathered connections
382  */
383 template <typename Callback>
384 void getConnections(
385     std::shared_ptr<SensorsAsyncResp> sensorsAsyncResp,
386     const std::shared_ptr<boost::container::flat_set<std::string>> sensorNames,
387     Callback&& callback)
388 {
389     auto objectsWithConnectionCb =
390         [callback](const boost::container::flat_set<std::string>& connections,
391                    const std::set<std::pair<std::string, std::string>>&
392                    /*objectsWithConnection*/) { callback(connections); };
393     getObjectsWithConnection(sensorsAsyncResp, sensorNames,
394                              std::move(objectsWithConnectionCb));
395 }
396 
397 /**
398  * @brief Shrinks the list of sensors for processing
399  * @param SensorsAysncResp  The class holding the Redfish response
400  * @param allSensors  A list of all the sensors associated to the
401  * chassis element (i.e. baseboard, front panel, etc...)
402  * @param activeSensors A list that is a reduction of the incoming
403  * allSensors list.  Eliminate Thermal sensors when a Power request is
404  * made, and eliminate Power sensors when a Thermal request is made.
405  */
406 inline void reduceSensorList(
407     const std::shared_ptr<SensorsAsyncResp>& sensorsAsyncResp,
408     const std::vector<std::string>* allSensors,
409     const std::shared_ptr<boost::container::flat_set<std::string>>&
410         activeSensors)
411 {
412     if (sensorsAsyncResp == nullptr)
413     {
414         return;
415     }
416     if ((allSensors == nullptr) || (activeSensors == nullptr))
417     {
418         messages::resourceNotFound(
419             sensorsAsyncResp->res, sensorsAsyncResp->chassisSubNode,
420             sensorsAsyncResp->chassisSubNode == sensors::node::thermal
421                 ? "Temperatures"
422                 : "Voltages");
423 
424         return;
425     }
426     if (allSensors->empty())
427     {
428         // Nothing to do, the activeSensors object is also empty
429         return;
430     }
431 
432     for (const char* type : sensorsAsyncResp->types)
433     {
434         for (const std::string& sensor : *allSensors)
435         {
436             if (boost::starts_with(sensor, type))
437             {
438                 activeSensors->emplace(sensor);
439             }
440         }
441     }
442 }
443 
444 /**
445  * @brief Retrieves valid chassis path
446  * @param asyncResp   Pointer to object holding response data
447  * @param callback  Callback for next step to get valid chassis path
448  */
449 template <typename Callback>
450 void getValidChassisPath(const std::shared_ptr<SensorsAsyncResp>& asyncResp,
451                          Callback&& callback)
452 {
453     BMCWEB_LOG_DEBUG << "checkChassisId enter";
454     const std::array<const char*, 2> interfaces = {
455         "xyz.openbmc_project.Inventory.Item.Board",
456         "xyz.openbmc_project.Inventory.Item.Chassis"};
457 
458     auto respHandler =
459         [callback{std::move(callback)},
460          asyncResp](const boost::system::error_code ec,
461                     const std::vector<std::string>& chassisPaths) mutable {
462             BMCWEB_LOG_DEBUG << "getValidChassisPath respHandler enter";
463             if (ec)
464             {
465                 BMCWEB_LOG_ERROR
466                     << "getValidChassisPath respHandler DBUS error: " << ec;
467                 messages::internalError(asyncResp->res);
468                 return;
469             }
470 
471             std::optional<std::string> chassisPath;
472             std::string chassisName;
473             for (const std::string& chassis : chassisPaths)
474             {
475                 sdbusplus::message::object_path path(chassis);
476                 chassisName = path.filename();
477                 if (chassisName.empty())
478                 {
479                     BMCWEB_LOG_ERROR << "Failed to find '/' in " << chassis;
480                     continue;
481                 }
482                 if (chassisName == asyncResp->chassisId)
483                 {
484                     chassisPath = chassis;
485                     break;
486                 }
487             }
488             callback(chassisPath);
489         };
490 
491     // Get the Chassis Collection
492     crow::connections::systemBus->async_method_call(
493         respHandler, "xyz.openbmc_project.ObjectMapper",
494         "/xyz/openbmc_project/object_mapper",
495         "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths",
496         "/xyz/openbmc_project/inventory", 0, interfaces);
497     BMCWEB_LOG_DEBUG << "checkChassisId exit";
498 }
499 
500 /**
501  * @brief Retrieves requested chassis sensors and redundancy data from DBus .
502  * @param SensorsAsyncResp   Pointer to object holding response data
503  * @param callback  Callback for next step in gathered sensor processing
504  */
505 template <typename Callback>
506 void getChassis(const std::shared_ptr<SensorsAsyncResp>& sensorsAsyncResp,
507                 Callback&& callback)
508 {
509     BMCWEB_LOG_DEBUG << "getChassis enter";
510     const std::array<const char*, 2> interfaces = {
511         "xyz.openbmc_project.Inventory.Item.Board",
512         "xyz.openbmc_project.Inventory.Item.Chassis"};
513     auto respHandler = [callback{std::move(callback)}, sensorsAsyncResp](
514                            const boost::system::error_code ec,
515                            const std::vector<std::string>& chassisPaths) {
516         BMCWEB_LOG_DEBUG << "getChassis respHandler enter";
517         if (ec)
518         {
519             BMCWEB_LOG_ERROR << "getChassis respHandler DBUS error: " << ec;
520             messages::internalError(sensorsAsyncResp->res);
521             return;
522         }
523 
524         const std::string* chassisPath = nullptr;
525         std::string chassisName;
526         for (const std::string& chassis : chassisPaths)
527         {
528             sdbusplus::message::object_path path(chassis);
529             chassisName = path.filename();
530             if (chassisName.empty())
531             {
532                 BMCWEB_LOG_ERROR << "Failed to find '/' in " << chassis;
533                 continue;
534             }
535             if (chassisName == sensorsAsyncResp->chassisId)
536             {
537                 chassisPath = &chassis;
538                 break;
539             }
540         }
541         if (chassisPath == nullptr)
542         {
543             messages::resourceNotFound(sensorsAsyncResp->res, "Chassis",
544                                        sensorsAsyncResp->chassisId);
545             return;
546         }
547 
548         const std::string& chassisSubNode = sensorsAsyncResp->chassisSubNode;
549         if (chassisSubNode == sensors::node::power)
550         {
551             sensorsAsyncResp->res.jsonValue["@odata.type"] =
552                 "#Power.v1_5_2.Power";
553         }
554         else if (chassisSubNode == sensors::node::thermal)
555         {
556             sensorsAsyncResp->res.jsonValue["@odata.type"] =
557                 "#Thermal.v1_4_0.Thermal";
558             sensorsAsyncResp->res.jsonValue["Fans"] = nlohmann::json::array();
559             sensorsAsyncResp->res.jsonValue["Temperatures"] =
560                 nlohmann::json::array();
561         }
562         else if (chassisSubNode == sensors::node::sensors)
563         {
564             sensorsAsyncResp->res.jsonValue["@odata.type"] =
565                 "#SensorCollection.SensorCollection";
566             sensorsAsyncResp->res.jsonValue["Description"] =
567                 "Collection of Sensors for this Chassis";
568             sensorsAsyncResp->res.jsonValue["Members"] =
569                 nlohmann::json::array();
570             sensorsAsyncResp->res.jsonValue["Members@odata.count"] = 0;
571         }
572 
573         if (chassisSubNode != sensors::node::sensors)
574         {
575             sensorsAsyncResp->res.jsonValue["Id"] = chassisSubNode;
576         }
577 
578         sensorsAsyncResp->res.jsonValue["@odata.id"] =
579             "/redfish/v1/Chassis/" + sensorsAsyncResp->chassisId + "/" +
580             chassisSubNode;
581         sensorsAsyncResp->res.jsonValue["Name"] = chassisSubNode;
582 
583         // Get the list of all sensors for this Chassis element
584         std::string sensorPath = *chassisPath + "/all_sensors";
585         crow::connections::systemBus->async_method_call(
586             [sensorsAsyncResp, callback{std::move(callback)}](
587                 const boost::system::error_code& e,
588                 const std::variant<std::vector<std::string>>&
589                     variantEndpoints) {
590                 if (e)
591                 {
592                     if (e.value() != EBADR)
593                     {
594                         messages::internalError(sensorsAsyncResp->res);
595                         return;
596                     }
597                 }
598                 const std::vector<std::string>* nodeSensorList =
599                     std::get_if<std::vector<std::string>>(&(variantEndpoints));
600                 if (nodeSensorList == nullptr)
601                 {
602                     messages::resourceNotFound(
603                         sensorsAsyncResp->res, sensorsAsyncResp->chassisSubNode,
604                         sensorsAsyncResp->chassisSubNode ==
605                                 sensors::node::thermal
606                             ? "Temperatures"
607                         : sensorsAsyncResp->chassisSubNode ==
608                                 sensors::node::power
609                             ? "Voltages"
610                             : "Sensors");
611                     return;
612                 }
613                 const std::shared_ptr<boost::container::flat_set<std::string>>
614                     culledSensorList = std::make_shared<
615                         boost::container::flat_set<std::string>>();
616                 reduceSensorList(sensorsAsyncResp, nodeSensorList,
617                                  culledSensorList);
618                 callback(culledSensorList);
619             },
620             "xyz.openbmc_project.ObjectMapper", sensorPath,
621             "org.freedesktop.DBus.Properties", "Get",
622             "xyz.openbmc_project.Association", "endpoints");
623     };
624 
625     // Get the Chassis Collection
626     crow::connections::systemBus->async_method_call(
627         respHandler, "xyz.openbmc_project.ObjectMapper",
628         "/xyz/openbmc_project/object_mapper",
629         "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths",
630         "/xyz/openbmc_project/inventory", 0, interfaces);
631     BMCWEB_LOG_DEBUG << "getChassis exit";
632 }
633 
634 /**
635  * @brief Finds all DBus object paths that implement ObjectManager.
636  *
637  * Creates a mapping from the associated connection name to the object path.
638  *
639  * Finds the object paths asynchronously.  Invokes callback when information has
640  * been obtained.
641  *
642  * The callback must have the following signature:
643  *   @code
644  *   callback(std::shared_ptr<boost::container::flat_map<std::string,
645  *                std::string>> objectMgrPaths)
646  *   @endcode
647  *
648  * @param sensorsAsyncResp Pointer to object holding response data.
649  * @param callback Callback to invoke when object paths obtained.
650  */
651 template <typename Callback>
652 void getObjectManagerPaths(
653     const std::shared_ptr<SensorsAsyncResp>& sensorsAsyncResp,
654     Callback&& callback)
655 {
656     BMCWEB_LOG_DEBUG << "getObjectManagerPaths enter";
657     const std::array<std::string, 1> interfaces = {
658         "org.freedesktop.DBus.ObjectManager"};
659 
660     // Response handler for GetSubTree DBus method
661     auto respHandler = [callback{std::move(callback)},
662                         sensorsAsyncResp](const boost::system::error_code ec,
663                                           const GetSubTreeType& subtree) {
664         BMCWEB_LOG_DEBUG << "getObjectManagerPaths respHandler enter";
665         if (ec)
666         {
667             messages::internalError(sensorsAsyncResp->res);
668             BMCWEB_LOG_ERROR << "getObjectManagerPaths respHandler: DBus error "
669                              << ec;
670             return;
671         }
672 
673         // Loop over returned object paths
674         std::shared_ptr<boost::container::flat_map<std::string, std::string>>
675             objectMgrPaths = std::make_shared<
676                 boost::container::flat_map<std::string, std::string>>();
677         for (const std::pair<
678                  std::string,
679                  std::vector<std::pair<std::string, std::vector<std::string>>>>&
680                  object : subtree)
681         {
682             // Loop over connections for current object path
683             const std::string& objectPath = object.first;
684             for (const std::pair<std::string, std::vector<std::string>>&
685                      objData : object.second)
686             {
687                 // Add mapping from connection to object path
688                 const std::string& connection = objData.first;
689                 (*objectMgrPaths)[connection] = objectPath;
690                 BMCWEB_LOG_DEBUG << "Added mapping " << connection << " -> "
691                                  << objectPath;
692             }
693         }
694         callback(objectMgrPaths);
695         BMCWEB_LOG_DEBUG << "getObjectManagerPaths respHandler exit";
696     };
697 
698     // Query mapper for all DBus object paths that implement ObjectManager
699     crow::connections::systemBus->async_method_call(
700         std::move(respHandler), "xyz.openbmc_project.ObjectMapper",
701         "/xyz/openbmc_project/object_mapper",
702         "xyz.openbmc_project.ObjectMapper", "GetSubTree", "/", 0, interfaces);
703     BMCWEB_LOG_DEBUG << "getObjectManagerPaths exit";
704 }
705 
706 /**
707  * @brief Returns the Redfish State value for the specified inventory item.
708  * @param inventoryItem D-Bus inventory item associated with a sensor.
709  * @return State value for inventory item.
710  */
711 inline std::string getState(const InventoryItem* inventoryItem)
712 {
713     if ((inventoryItem != nullptr) && !(inventoryItem->isPresent))
714     {
715         return "Absent";
716     }
717 
718     return "Enabled";
719 }
720 
721 /**
722  * @brief Returns the Redfish Health value for the specified sensor.
723  * @param sensorJson Sensor JSON object.
724  * @param interfacesDict Map of all sensor interfaces.
725  * @param inventoryItem D-Bus inventory item associated with the sensor.  Will
726  * be nullptr if no associated inventory item was found.
727  * @return Health value for sensor.
728  */
729 inline std::string getHealth(
730     nlohmann::json& sensorJson,
731     const boost::container::flat_map<
732         std::string, boost::container::flat_map<std::string, SensorVariant>>&
733         interfacesDict,
734     const InventoryItem* inventoryItem)
735 {
736     // Get current health value (if any) in the sensor JSON object.  Some JSON
737     // objects contain multiple sensors (such as PowerSupplies).  We want to set
738     // the overall health to be the most severe of any of the sensors.
739     std::string currentHealth;
740     auto statusIt = sensorJson.find("Status");
741     if (statusIt != sensorJson.end())
742     {
743         auto healthIt = statusIt->find("Health");
744         if (healthIt != statusIt->end())
745         {
746             std::string* health = healthIt->get_ptr<std::string*>();
747             if (health != nullptr)
748             {
749                 currentHealth = *health;
750             }
751         }
752     }
753 
754     // If current health in JSON object is already Critical, return that.  This
755     // should override the sensor health, which might be less severe.
756     if (currentHealth == "Critical")
757     {
758         return "Critical";
759     }
760 
761     // Check if sensor has critical threshold alarm
762     auto criticalThresholdIt =
763         interfacesDict.find("xyz.openbmc_project.Sensor.Threshold.Critical");
764     if (criticalThresholdIt != interfacesDict.end())
765     {
766         auto thresholdHighIt =
767             criticalThresholdIt->second.find("CriticalAlarmHigh");
768         auto thresholdLowIt =
769             criticalThresholdIt->second.find("CriticalAlarmLow");
770         if (thresholdHighIt != criticalThresholdIt->second.end())
771         {
772             const bool* asserted = std::get_if<bool>(&thresholdHighIt->second);
773             if (asserted == nullptr)
774             {
775                 BMCWEB_LOG_ERROR << "Illegal sensor threshold";
776             }
777             else if (*asserted)
778             {
779                 return "Critical";
780             }
781         }
782         if (thresholdLowIt != criticalThresholdIt->second.end())
783         {
784             const bool* asserted = std::get_if<bool>(&thresholdLowIt->second);
785             if (asserted == nullptr)
786             {
787                 BMCWEB_LOG_ERROR << "Illegal sensor threshold";
788             }
789             else if (*asserted)
790             {
791                 return "Critical";
792             }
793         }
794     }
795 
796     // Check if associated inventory item is not functional
797     if ((inventoryItem != nullptr) && !(inventoryItem->isFunctional))
798     {
799         return "Critical";
800     }
801 
802     // If current health in JSON object is already Warning, return that.  This
803     // should override the sensor status, which might be less severe.
804     if (currentHealth == "Warning")
805     {
806         return "Warning";
807     }
808 
809     // Check if sensor has warning threshold alarm
810     auto warningThresholdIt =
811         interfacesDict.find("xyz.openbmc_project.Sensor.Threshold.Warning");
812     if (warningThresholdIt != interfacesDict.end())
813     {
814         auto thresholdHighIt =
815             warningThresholdIt->second.find("WarningAlarmHigh");
816         auto thresholdLowIt =
817             warningThresholdIt->second.find("WarningAlarmLow");
818         if (thresholdHighIt != warningThresholdIt->second.end())
819         {
820             const bool* asserted = std::get_if<bool>(&thresholdHighIt->second);
821             if (asserted == nullptr)
822             {
823                 BMCWEB_LOG_ERROR << "Illegal sensor threshold";
824             }
825             else if (*asserted)
826             {
827                 return "Warning";
828             }
829         }
830         if (thresholdLowIt != warningThresholdIt->second.end())
831         {
832             const bool* asserted = std::get_if<bool>(&thresholdLowIt->second);
833             if (asserted == nullptr)
834             {
835                 BMCWEB_LOG_ERROR << "Illegal sensor threshold";
836             }
837             else if (*asserted)
838             {
839                 return "Warning";
840             }
841         }
842     }
843 
844     return "OK";
845 }
846 
847 inline void setLedState(nlohmann::json& sensorJson,
848                         const InventoryItem* inventoryItem)
849 {
850     if (inventoryItem != nullptr && !inventoryItem->ledObjectPath.empty())
851     {
852         switch (inventoryItem->ledState)
853         {
854             case LedState::OFF:
855                 sensorJson["IndicatorLED"] = "Off";
856                 break;
857             case LedState::ON:
858                 sensorJson["IndicatorLED"] = "Lit";
859                 break;
860             case LedState::BLINK:
861                 sensorJson["IndicatorLED"] = "Blinking";
862                 break;
863             case LedState::UNKNOWN:
864                 break;
865         }
866     }
867 }
868 
869 /**
870  * @brief Builds a json sensor representation of a sensor.
871  * @param sensorName  The name of the sensor to be built
872  * @param sensorType  The type (temperature, fan_tach, etc) of the sensor to
873  * build
874  * @param sensorsAsyncResp  Sensor metadata
875  * @param interfacesDict  A dictionary of the interfaces and properties of said
876  * interfaces to be built from
877  * @param sensor_json  The json object to fill
878  * @param inventoryItem D-Bus inventory item associated with the sensor.  Will
879  * be nullptr if no associated inventory item was found.
880  */
881 inline void objectInterfacesToJson(
882     const std::string& sensorName, const std::string& sensorType,
883     const std::shared_ptr<SensorsAsyncResp>& sensorsAsyncResp,
884     const boost::container::flat_map<
885         std::string, boost::container::flat_map<std::string, SensorVariant>>&
886         interfacesDict,
887     nlohmann::json& sensorJson, InventoryItem* inventoryItem)
888 {
889     // We need a value interface before we can do anything with it
890     auto valueIt = interfacesDict.find("xyz.openbmc_project.Sensor.Value");
891     if (valueIt == interfacesDict.end())
892     {
893         BMCWEB_LOG_ERROR << "Sensor doesn't have a value interface";
894         return;
895     }
896 
897     // Assume values exist as is (10^0 == 1) if no scale exists
898     int64_t scaleMultiplier = 0;
899 
900     auto scaleIt = valueIt->second.find("Scale");
901     // If a scale exists, pull value as int64, and use the scaling.
902     if (scaleIt != valueIt->second.end())
903     {
904         const int64_t* int64Value = std::get_if<int64_t>(&scaleIt->second);
905         if (int64Value != nullptr)
906         {
907             scaleMultiplier = *int64Value;
908         }
909     }
910 
911     if (sensorsAsyncResp->chassisSubNode == sensors::node::sensors)
912     {
913         // For sensors in SensorCollection we set Id instead of MemberId,
914         // including power sensors.
915         sensorJson["Id"] = sensorName;
916         sensorJson["Name"] = boost::replace_all_copy(sensorName, "_", " ");
917     }
918     else if (sensorType != "power")
919     {
920         // Set MemberId and Name for non-power sensors.  For PowerSupplies and
921         // PowerControl, those properties have more general values because
922         // multiple sensors can be stored in the same JSON object.
923         sensorJson["MemberId"] = sensorName;
924         sensorJson["Name"] = boost::replace_all_copy(sensorName, "_", " ");
925     }
926 
927     sensorJson["Status"]["State"] = getState(inventoryItem);
928     sensorJson["Status"]["Health"] =
929         getHealth(sensorJson, interfacesDict, inventoryItem);
930 
931     // Parameter to set to override the type we get from dbus, and force it to
932     // int, regardless of what is available.  This is used for schemas like fan,
933     // that require integers, not floats.
934     bool forceToInt = false;
935 
936     nlohmann::json::json_pointer unit("/Reading");
937     if (sensorsAsyncResp->chassisSubNode == sensors::node::sensors)
938     {
939         sensorJson["@odata.type"] = "#Sensor.v1_0_0.Sensor";
940 
941         const std::string& readingType = sensors::toReadingType(sensorType);
942         if (readingType.empty())
943         {
944             BMCWEB_LOG_ERROR << "Redfish cannot map reading type for "
945                              << sensorType;
946         }
947         else
948         {
949             sensorJson["ReadingType"] = readingType;
950         }
951 
952         const std::string& readingUnits = sensors::toReadingUnits(sensorType);
953         if (readingUnits.empty())
954         {
955             BMCWEB_LOG_ERROR << "Redfish cannot map reading unit for "
956                              << sensorType;
957         }
958         else
959         {
960             sensorJson["ReadingUnits"] = readingUnits;
961         }
962     }
963     else if (sensorType == "temperature")
964     {
965         unit = "/ReadingCelsius"_json_pointer;
966         sensorJson["@odata.type"] = "#Thermal.v1_3_0.Temperature";
967         // TODO(ed) Documentation says that path should be type fan_tach,
968         // implementation seems to implement fan
969     }
970     else if (sensorType == "fan" || sensorType == "fan_tach")
971     {
972         unit = "/Reading"_json_pointer;
973         sensorJson["ReadingUnits"] = "RPM";
974         sensorJson["@odata.type"] = "#Thermal.v1_3_0.Fan";
975         setLedState(sensorJson, inventoryItem);
976         forceToInt = true;
977     }
978     else if (sensorType == "fan_pwm")
979     {
980         unit = "/Reading"_json_pointer;
981         sensorJson["ReadingUnits"] = "Percent";
982         sensorJson["@odata.type"] = "#Thermal.v1_3_0.Fan";
983         setLedState(sensorJson, inventoryItem);
984         forceToInt = true;
985     }
986     else if (sensorType == "voltage")
987     {
988         unit = "/ReadingVolts"_json_pointer;
989         sensorJson["@odata.type"] = "#Power.v1_0_0.Voltage";
990     }
991     else if (sensorType == "power")
992     {
993         std::string sensorNameLower =
994             boost::algorithm::to_lower_copy(sensorName);
995 
996         if (!sensorName.compare("total_power"))
997         {
998             sensorJson["@odata.type"] = "#Power.v1_0_0.PowerControl";
999             // Put multiple "sensors" into a single PowerControl, so have
1000             // generic names for MemberId and Name. Follows Redfish mockup.
1001             sensorJson["MemberId"] = "0";
1002             sensorJson["Name"] = "Chassis Power Control";
1003             unit = "/PowerConsumedWatts"_json_pointer;
1004         }
1005         else if (sensorNameLower.find("input") != std::string::npos)
1006         {
1007             unit = "/PowerInputWatts"_json_pointer;
1008         }
1009         else
1010         {
1011             unit = "/PowerOutputWatts"_json_pointer;
1012         }
1013     }
1014     else
1015     {
1016         BMCWEB_LOG_ERROR << "Redfish cannot map object type for " << sensorName;
1017         return;
1018     }
1019     // Map of dbus interface name, dbus property name and redfish property_name
1020     std::vector<
1021         std::tuple<const char*, const char*, nlohmann::json::json_pointer>>
1022         properties;
1023     properties.reserve(7);
1024 
1025     properties.emplace_back("xyz.openbmc_project.Sensor.Value", "Value", unit);
1026 
1027     if (sensorsAsyncResp->chassisSubNode == sensors::node::sensors)
1028     {
1029         properties.emplace_back(
1030             "xyz.openbmc_project.Sensor.Threshold.Warning", "WarningHigh",
1031             "/Thresholds/UpperCaution/Reading"_json_pointer);
1032         properties.emplace_back(
1033             "xyz.openbmc_project.Sensor.Threshold.Warning", "WarningLow",
1034             "/Thresholds/LowerCaution/Reading"_json_pointer);
1035         properties.emplace_back(
1036             "xyz.openbmc_project.Sensor.Threshold.Critical", "CriticalHigh",
1037             "/Thresholds/UpperCritical/Reading"_json_pointer);
1038         properties.emplace_back(
1039             "xyz.openbmc_project.Sensor.Threshold.Critical", "CriticalLow",
1040             "/Thresholds/LowerCritical/Reading"_json_pointer);
1041     }
1042     else if (sensorType != "power")
1043     {
1044         properties.emplace_back("xyz.openbmc_project.Sensor.Threshold.Warning",
1045                                 "WarningHigh",
1046                                 "/UpperThresholdNonCritical"_json_pointer);
1047         properties.emplace_back("xyz.openbmc_project.Sensor.Threshold.Warning",
1048                                 "WarningLow",
1049                                 "/LowerThresholdNonCritical"_json_pointer);
1050         properties.emplace_back("xyz.openbmc_project.Sensor.Threshold.Critical",
1051                                 "CriticalHigh",
1052                                 "/UpperThresholdCritical"_json_pointer);
1053         properties.emplace_back("xyz.openbmc_project.Sensor.Threshold.Critical",
1054                                 "CriticalLow",
1055                                 "/LowerThresholdCritical"_json_pointer);
1056     }
1057 
1058     // TODO Need to get UpperThresholdFatal and LowerThresholdFatal
1059 
1060     if (sensorsAsyncResp->chassisSubNode == sensors::node::sensors)
1061     {
1062         properties.emplace_back("xyz.openbmc_project.Sensor.Value", "MinValue",
1063                                 "/ReadingRangeMin"_json_pointer);
1064         properties.emplace_back("xyz.openbmc_project.Sensor.Value", "MaxValue",
1065                                 "/ReadingRangeMax"_json_pointer);
1066     }
1067     else if (sensorType == "temperature")
1068     {
1069         properties.emplace_back("xyz.openbmc_project.Sensor.Value", "MinValue",
1070                                 "/MinReadingRangeTemp"_json_pointer);
1071         properties.emplace_back("xyz.openbmc_project.Sensor.Value", "MaxValue",
1072                                 "/MaxReadingRangeTemp"_json_pointer);
1073     }
1074     else if (sensorType != "power")
1075     {
1076         properties.emplace_back("xyz.openbmc_project.Sensor.Value", "MinValue",
1077                                 "/MinReadingRange"_json_pointer);
1078         properties.emplace_back("xyz.openbmc_project.Sensor.Value", "MaxValue",
1079                                 "/MaxReadingRange"_json_pointer);
1080     }
1081 
1082     for (const std::tuple<const char*, const char*,
1083                           nlohmann::json::json_pointer>& p : properties)
1084     {
1085         auto interfaceProperties = interfacesDict.find(std::get<0>(p));
1086         if (interfaceProperties != interfacesDict.end())
1087         {
1088             auto thisValueIt = interfaceProperties->second.find(std::get<1>(p));
1089             if (thisValueIt != interfaceProperties->second.end())
1090             {
1091                 const SensorVariant& valueVariant = thisValueIt->second;
1092 
1093                 // The property we want to set may be nested json, so use
1094                 // a json_pointer for easy indexing into the json structure.
1095                 const nlohmann::json::json_pointer& key = std::get<2>(p);
1096 
1097                 // Attempt to pull the int64 directly
1098                 const int64_t* int64Value = std::get_if<int64_t>(&valueVariant);
1099 
1100                 const double* doubleValue = std::get_if<double>(&valueVariant);
1101                 const uint32_t* uValue = std::get_if<uint32_t>(&valueVariant);
1102                 double temp = 0.0;
1103                 if (int64Value != nullptr)
1104                 {
1105                     temp = static_cast<double>(*int64Value);
1106                 }
1107                 else if (doubleValue != nullptr)
1108                 {
1109                     temp = *doubleValue;
1110                 }
1111                 else if (uValue != nullptr)
1112                 {
1113                     temp = *uValue;
1114                 }
1115                 else
1116                 {
1117                     BMCWEB_LOG_ERROR
1118                         << "Got value interface that wasn't int or double";
1119                     continue;
1120                 }
1121                 temp = temp * std::pow(10, scaleMultiplier);
1122                 if (forceToInt)
1123                 {
1124                     sensorJson[key] = static_cast<int64_t>(temp);
1125                 }
1126                 else
1127                 {
1128                     sensorJson[key] = temp;
1129                 }
1130             }
1131         }
1132     }
1133 
1134     sensorsAsyncResp->addMetadata(sensorJson, unit.to_string(),
1135                                   "/xyz/openbmc_project/sensors/" + sensorType +
1136                                       "/" + sensorName);
1137 
1138     BMCWEB_LOG_DEBUG << "Added sensor " << sensorName;
1139 }
1140 
1141 inline void populateFanRedundancy(
1142     const std::shared_ptr<SensorsAsyncResp>& sensorsAsyncResp)
1143 {
1144     crow::connections::systemBus->async_method_call(
1145         [sensorsAsyncResp](const boost::system::error_code ec,
1146                            const GetSubTreeType& resp) {
1147             if (ec)
1148             {
1149                 return; // don't have to have this interface
1150             }
1151             for (const std::pair<std::string,
1152                                  std::vector<std::pair<
1153                                      std::string, std::vector<std::string>>>>&
1154                      pathPair : resp)
1155             {
1156                 const std::string& path = pathPair.first;
1157                 const std::vector<
1158                     std::pair<std::string, std::vector<std::string>>>& objDict =
1159                     pathPair.second;
1160                 if (objDict.empty())
1161                 {
1162                     continue; // this should be impossible
1163                 }
1164 
1165                 const std::string& owner = objDict.begin()->first;
1166                 crow::connections::systemBus->async_method_call(
1167                     [path, owner,
1168                      sensorsAsyncResp](const boost::system::error_code e,
1169                                        std::variant<std::vector<std::string>>
1170                                            variantEndpoints) {
1171                         if (e)
1172                         {
1173                             return; // if they don't have an association we
1174                                     // can't tell what chassis is
1175                         }
1176                         // verify part of the right chassis
1177                         auto endpoints = std::get_if<std::vector<std::string>>(
1178                             &variantEndpoints);
1179 
1180                         if (endpoints == nullptr)
1181                         {
1182                             BMCWEB_LOG_ERROR << "Invalid association interface";
1183                             messages::internalError(sensorsAsyncResp->res);
1184                             return;
1185                         }
1186 
1187                         auto found = std::find_if(
1188                             endpoints->begin(), endpoints->end(),
1189                             [sensorsAsyncResp](const std::string& entry) {
1190                                 return entry.find(
1191                                            sensorsAsyncResp->chassisId) !=
1192                                        std::string::npos;
1193                             });
1194 
1195                         if (found == endpoints->end())
1196                         {
1197                             return;
1198                         }
1199                         crow::connections::systemBus->async_method_call(
1200                             [path, sensorsAsyncResp](
1201                                 const boost::system::error_code& err,
1202                                 const boost::container::flat_map<
1203                                     std::string,
1204                                     std::variant<uint8_t,
1205                                                  std::vector<std::string>,
1206                                                  std::string>>& ret) {
1207                                 if (err)
1208                                 {
1209                                     return; // don't have to have this
1210                                             // interface
1211                                 }
1212                                 auto findFailures = ret.find("AllowedFailures");
1213                                 auto findCollection = ret.find("Collection");
1214                                 auto findStatus = ret.find("Status");
1215 
1216                                 if (findFailures == ret.end() ||
1217                                     findCollection == ret.end() ||
1218                                     findStatus == ret.end())
1219                                 {
1220                                     BMCWEB_LOG_ERROR
1221                                         << "Invalid redundancy interface";
1222                                     messages::internalError(
1223                                         sensorsAsyncResp->res);
1224                                     return;
1225                                 }
1226 
1227                                 auto allowedFailures = std::get_if<uint8_t>(
1228                                     &(findFailures->second));
1229                                 auto collection =
1230                                     std::get_if<std::vector<std::string>>(
1231                                         &(findCollection->second));
1232                                 auto status = std::get_if<std::string>(
1233                                     &(findStatus->second));
1234 
1235                                 if (allowedFailures == nullptr ||
1236                                     collection == nullptr || status == nullptr)
1237                                 {
1238 
1239                                     BMCWEB_LOG_ERROR
1240                                         << "Invalid redundancy interface "
1241                                            "types";
1242                                     messages::internalError(
1243                                         sensorsAsyncResp->res);
1244                                     return;
1245                                 }
1246                                 sdbusplus::message::object_path objectPath(
1247                                     path);
1248                                 std::string name = objectPath.filename();
1249                                 if (name.empty())
1250                                 {
1251                                     // this should be impossible
1252                                     messages::internalError(
1253                                         sensorsAsyncResp->res);
1254                                     return;
1255                                 }
1256                                 std::replace(name.begin(), name.end(), '_',
1257                                              ' ');
1258 
1259                                 std::string health;
1260 
1261                                 if (boost::ends_with(*status, "Full"))
1262                                 {
1263                                     health = "OK";
1264                                 }
1265                                 else if (boost::ends_with(*status, "Degraded"))
1266                                 {
1267                                     health = "Warning";
1268                                 }
1269                                 else
1270                                 {
1271                                     health = "Critical";
1272                                 }
1273                                 std::vector<nlohmann::json> redfishCollection;
1274                                 const auto& fanRedfish =
1275                                     sensorsAsyncResp->res.jsonValue["Fans"];
1276                                 for (const std::string& item : *collection)
1277                                 {
1278                                     sdbusplus::message::object_path path(item);
1279                                     std::string itemName = path.filename();
1280                                     if (itemName.empty())
1281                                     {
1282                                         continue;
1283                                     }
1284                                     /*
1285                                     todo(ed): merge patch that fixes the names
1286                                     std::replace(itemName.begin(),
1287                                                  itemName.end(), '_', ' ');*/
1288                                     auto schemaItem = std::find_if(
1289                                         fanRedfish.begin(), fanRedfish.end(),
1290                                         [itemName](const nlohmann::json& fan) {
1291                                             return fan["MemberId"] == itemName;
1292                                         });
1293                                     if (schemaItem != fanRedfish.end())
1294                                     {
1295                                         redfishCollection.push_back(
1296                                             {{"@odata.id",
1297                                               (*schemaItem)["@odata.id"]}});
1298                                     }
1299                                     else
1300                                     {
1301                                         BMCWEB_LOG_ERROR
1302                                             << "failed to find fan in schema";
1303                                         messages::internalError(
1304                                             sensorsAsyncResp->res);
1305                                         return;
1306                                     }
1307                                 }
1308 
1309                                 size_t minNumNeeded =
1310                                     collection->size() > 0
1311                                         ? collection->size() - *allowedFailures
1312                                         : 0;
1313                                 nlohmann::json& jResp =
1314                                     sensorsAsyncResp->res
1315                                         .jsonValue["Redundancy"];
1316                                 jResp.push_back(
1317                                     {{"@odata.id",
1318                                       "/redfish/v1/Chassis/" +
1319                                           sensorsAsyncResp->chassisId + "/" +
1320                                           sensorsAsyncResp->chassisSubNode +
1321                                           "#/Redundancy/" +
1322                                           std::to_string(jResp.size())},
1323                                      {"@odata.type",
1324                                       "#Redundancy.v1_3_2.Redundancy"},
1325                                      {"MinNumNeeded", minNumNeeded},
1326                                      {"MemberId", name},
1327                                      {"Mode", "N+m"},
1328                                      {"Name", name},
1329                                      {"RedundancySet", redfishCollection},
1330                                      {"Status",
1331                                       {{"Health", health},
1332                                        {"State", "Enabled"}}}});
1333                             },
1334                             owner, path, "org.freedesktop.DBus.Properties",
1335                             "GetAll",
1336                             "xyz.openbmc_project.Control.FanRedundancy");
1337                     },
1338                     "xyz.openbmc_project.ObjectMapper", path + "/chassis",
1339                     "org.freedesktop.DBus.Properties", "Get",
1340                     "xyz.openbmc_project.Association", "endpoints");
1341             }
1342         },
1343         "xyz.openbmc_project.ObjectMapper",
1344         "/xyz/openbmc_project/object_mapper",
1345         "xyz.openbmc_project.ObjectMapper", "GetSubTree",
1346         "/xyz/openbmc_project/control", 2,
1347         std::array<const char*, 1>{
1348             "xyz.openbmc_project.Control.FanRedundancy"});
1349 }
1350 
1351 inline void
1352     sortJSONResponse(const std::shared_ptr<SensorsAsyncResp>& sensorsAsyncResp)
1353 {
1354     nlohmann::json& response = sensorsAsyncResp->res.jsonValue;
1355     std::array<std::string, 2> sensorHeaders{"Temperatures", "Fans"};
1356     if (sensorsAsyncResp->chassisSubNode == sensors::node::power)
1357     {
1358         sensorHeaders = {"Voltages", "PowerSupplies"};
1359     }
1360     for (const std::string& sensorGroup : sensorHeaders)
1361     {
1362         nlohmann::json::iterator entry = response.find(sensorGroup);
1363         if (entry != response.end())
1364         {
1365             std::sort(entry->begin(), entry->end(),
1366                       [](nlohmann::json& c1, nlohmann::json& c2) {
1367                           return c1["Name"] < c2["Name"];
1368                       });
1369 
1370             // add the index counts to the end of each entry
1371             size_t count = 0;
1372             for (nlohmann::json& sensorJson : *entry)
1373             {
1374                 nlohmann::json::iterator odata = sensorJson.find("@odata.id");
1375                 if (odata == sensorJson.end())
1376                 {
1377                     continue;
1378                 }
1379                 std::string* value = odata->get_ptr<std::string*>();
1380                 if (value != nullptr)
1381                 {
1382                     *value += std::to_string(count);
1383                     count++;
1384                     sensorsAsyncResp->updateUri(sensorJson["Name"], *value);
1385                 }
1386             }
1387         }
1388     }
1389 }
1390 
1391 /**
1392  * @brief Finds the inventory item with the specified object path.
1393  * @param inventoryItems D-Bus inventory items associated with sensors.
1394  * @param invItemObjPath D-Bus object path of inventory item.
1395  * @return Inventory item within vector, or nullptr if no match found.
1396  */
1397 inline InventoryItem* findInventoryItem(
1398     const std::shared_ptr<std::vector<InventoryItem>>& inventoryItems,
1399     const std::string& invItemObjPath)
1400 {
1401     for (InventoryItem& inventoryItem : *inventoryItems)
1402     {
1403         if (inventoryItem.objectPath == invItemObjPath)
1404         {
1405             return &inventoryItem;
1406         }
1407     }
1408     return nullptr;
1409 }
1410 
1411 /**
1412  * @brief Finds the inventory item associated with the specified sensor.
1413  * @param inventoryItems D-Bus inventory items associated with sensors.
1414  * @param sensorObjPath D-Bus object path of sensor.
1415  * @return Inventory item within vector, or nullptr if no match found.
1416  */
1417 inline InventoryItem* findInventoryItemForSensor(
1418     const std::shared_ptr<std::vector<InventoryItem>>& inventoryItems,
1419     const std::string& sensorObjPath)
1420 {
1421     for (InventoryItem& inventoryItem : *inventoryItems)
1422     {
1423         if (inventoryItem.sensors.count(sensorObjPath) > 0)
1424         {
1425             return &inventoryItem;
1426         }
1427     }
1428     return nullptr;
1429 }
1430 
1431 /**
1432  * @brief Finds the inventory item associated with the specified led path.
1433  * @param inventoryItems D-Bus inventory items associated with sensors.
1434  * @param ledObjPath D-Bus object path of led.
1435  * @return Inventory item within vector, or nullptr if no match found.
1436  */
1437 inline InventoryItem*
1438     findInventoryItemForLed(std::vector<InventoryItem>& inventoryItems,
1439                             const std::string& ledObjPath)
1440 {
1441     for (InventoryItem& inventoryItem : inventoryItems)
1442     {
1443         if (inventoryItem.ledObjectPath == ledObjPath)
1444         {
1445             return &inventoryItem;
1446         }
1447     }
1448     return nullptr;
1449 }
1450 
1451 /**
1452  * @brief Adds inventory item and associated sensor to specified vector.
1453  *
1454  * Adds a new InventoryItem to the vector if necessary.  Searches for an
1455  * existing InventoryItem with the specified object path.  If not found, one is
1456  * added to the vector.
1457  *
1458  * Next, the specified sensor is added to the set of sensors associated with the
1459  * InventoryItem.
1460  *
1461  * @param inventoryItems D-Bus inventory items associated with sensors.
1462  * @param invItemObjPath D-Bus object path of inventory item.
1463  * @param sensorObjPath D-Bus object path of sensor
1464  */
1465 inline void addInventoryItem(
1466     const std::shared_ptr<std::vector<InventoryItem>>& inventoryItems,
1467     const std::string& invItemObjPath, const std::string& sensorObjPath)
1468 {
1469     // Look for inventory item in vector
1470     InventoryItem* inventoryItem =
1471         findInventoryItem(inventoryItems, invItemObjPath);
1472 
1473     // If inventory item doesn't exist in vector, add it
1474     if (inventoryItem == nullptr)
1475     {
1476         inventoryItems->emplace_back(invItemObjPath);
1477         inventoryItem = &(inventoryItems->back());
1478     }
1479 
1480     // Add sensor to set of sensors associated with inventory item
1481     inventoryItem->sensors.emplace(sensorObjPath);
1482 }
1483 
1484 /**
1485  * @brief Stores D-Bus data in the specified inventory item.
1486  *
1487  * Finds D-Bus data in the specified map of interfaces.  Stores the data in the
1488  * specified InventoryItem.
1489  *
1490  * This data is later used to provide sensor property values in the JSON
1491  * response.
1492  *
1493  * @param inventoryItem Inventory item where data will be stored.
1494  * @param interfacesDict Map containing D-Bus interfaces and their properties
1495  * for the specified inventory item.
1496  */
1497 inline void storeInventoryItemData(
1498     InventoryItem& inventoryItem,
1499     const boost::container::flat_map<
1500         std::string, boost::container::flat_map<std::string, SensorVariant>>&
1501         interfacesDict)
1502 {
1503     // Get properties from Inventory.Item interface
1504     auto interfaceIt =
1505         interfacesDict.find("xyz.openbmc_project.Inventory.Item");
1506     if (interfaceIt != interfacesDict.end())
1507     {
1508         auto propertyIt = interfaceIt->second.find("Present");
1509         if (propertyIt != interfaceIt->second.end())
1510         {
1511             const bool* value = std::get_if<bool>(&propertyIt->second);
1512             if (value != nullptr)
1513             {
1514                 inventoryItem.isPresent = *value;
1515             }
1516         }
1517     }
1518 
1519     // Check if Inventory.Item.PowerSupply interface is present
1520     interfaceIt =
1521         interfacesDict.find("xyz.openbmc_project.Inventory.Item.PowerSupply");
1522     if (interfaceIt != interfacesDict.end())
1523     {
1524         inventoryItem.isPowerSupply = true;
1525     }
1526 
1527     // Get properties from Inventory.Decorator.Asset interface
1528     interfaceIt =
1529         interfacesDict.find("xyz.openbmc_project.Inventory.Decorator.Asset");
1530     if (interfaceIt != interfacesDict.end())
1531     {
1532         auto propertyIt = interfaceIt->second.find("Manufacturer");
1533         if (propertyIt != interfaceIt->second.end())
1534         {
1535             const std::string* value =
1536                 std::get_if<std::string>(&propertyIt->second);
1537             if (value != nullptr)
1538             {
1539                 inventoryItem.manufacturer = *value;
1540             }
1541         }
1542 
1543         propertyIt = interfaceIt->second.find("Model");
1544         if (propertyIt != interfaceIt->second.end())
1545         {
1546             const std::string* value =
1547                 std::get_if<std::string>(&propertyIt->second);
1548             if (value != nullptr)
1549             {
1550                 inventoryItem.model = *value;
1551             }
1552         }
1553 
1554         propertyIt = interfaceIt->second.find("PartNumber");
1555         if (propertyIt != interfaceIt->second.end())
1556         {
1557             const std::string* value =
1558                 std::get_if<std::string>(&propertyIt->second);
1559             if (value != nullptr)
1560             {
1561                 inventoryItem.partNumber = *value;
1562             }
1563         }
1564 
1565         propertyIt = interfaceIt->second.find("SerialNumber");
1566         if (propertyIt != interfaceIt->second.end())
1567         {
1568             const std::string* value =
1569                 std::get_if<std::string>(&propertyIt->second);
1570             if (value != nullptr)
1571             {
1572                 inventoryItem.serialNumber = *value;
1573             }
1574         }
1575     }
1576 
1577     // Get properties from State.Decorator.OperationalStatus interface
1578     interfaceIt = interfacesDict.find(
1579         "xyz.openbmc_project.State.Decorator.OperationalStatus");
1580     if (interfaceIt != interfacesDict.end())
1581     {
1582         auto propertyIt = interfaceIt->second.find("Functional");
1583         if (propertyIt != interfaceIt->second.end())
1584         {
1585             const bool* value = std::get_if<bool>(&propertyIt->second);
1586             if (value != nullptr)
1587             {
1588                 inventoryItem.isFunctional = *value;
1589             }
1590         }
1591     }
1592 }
1593 
1594 /**
1595  * @brief Gets D-Bus data for inventory items associated with sensors.
1596  *
1597  * Uses the specified connections (services) to obtain D-Bus data for inventory
1598  * items associated with sensors.  Stores the resulting data in the
1599  * inventoryItems vector.
1600  *
1601  * This data is later used to provide sensor property values in the JSON
1602  * response.
1603  *
1604  * Finds the inventory item data asynchronously.  Invokes callback when data has
1605  * been obtained.
1606  *
1607  * The callback must have the following signature:
1608  *   @code
1609  *   callback(void)
1610  *   @endcode
1611  *
1612  * This function is called recursively, obtaining data asynchronously from one
1613  * connection in each call.  This ensures the callback is not invoked until the
1614  * last asynchronous function has completed.
1615  *
1616  * @param sensorsAsyncResp Pointer to object holding response data.
1617  * @param inventoryItems D-Bus inventory items associated with sensors.
1618  * @param invConnections Connections that provide data for the inventory items.
1619  * @param objectMgrPaths Mappings from connection name to DBus object path that
1620  * implements ObjectManager.
1621  * @param callback Callback to invoke when inventory data has been obtained.
1622  * @param invConnectionsIndex Current index in invConnections.  Only specified
1623  * in recursive calls to this function.
1624  */
1625 template <typename Callback>
1626 static void getInventoryItemsData(
1627     std::shared_ptr<SensorsAsyncResp> sensorsAsyncResp,
1628     std::shared_ptr<std::vector<InventoryItem>> inventoryItems,
1629     std::shared_ptr<boost::container::flat_set<std::string>> invConnections,
1630     std::shared_ptr<boost::container::flat_map<std::string, std::string>>
1631         objectMgrPaths,
1632     Callback&& callback, size_t invConnectionsIndex = 0)
1633 {
1634     BMCWEB_LOG_DEBUG << "getInventoryItemsData enter";
1635 
1636     // If no more connections left, call callback
1637     if (invConnectionsIndex >= invConnections->size())
1638     {
1639         callback();
1640         BMCWEB_LOG_DEBUG << "getInventoryItemsData exit";
1641         return;
1642     }
1643 
1644     // Get inventory item data from current connection
1645     auto it = invConnections->nth(invConnectionsIndex);
1646     if (it != invConnections->end())
1647     {
1648         const std::string& invConnection = *it;
1649 
1650         // Response handler for GetManagedObjects
1651         auto respHandler = [sensorsAsyncResp, inventoryItems, invConnections,
1652                             objectMgrPaths, callback{std::move(callback)},
1653                             invConnectionsIndex](
1654                                const boost::system::error_code ec,
1655                                ManagedObjectsVectorType& resp) {
1656             BMCWEB_LOG_DEBUG << "getInventoryItemsData respHandler enter";
1657             if (ec)
1658             {
1659                 BMCWEB_LOG_ERROR
1660                     << "getInventoryItemsData respHandler DBus error " << ec;
1661                 messages::internalError(sensorsAsyncResp->res);
1662                 return;
1663             }
1664 
1665             // Loop through returned object paths
1666             for (const auto& objDictEntry : resp)
1667             {
1668                 const std::string& objPath =
1669                     static_cast<const std::string&>(objDictEntry.first);
1670 
1671                 // If this object path is one of the specified inventory items
1672                 InventoryItem* inventoryItem =
1673                     findInventoryItem(inventoryItems, objPath);
1674                 if (inventoryItem != nullptr)
1675                 {
1676                     // Store inventory data in InventoryItem
1677                     storeInventoryItemData(*inventoryItem, objDictEntry.second);
1678                 }
1679             }
1680 
1681             // Recurse to get inventory item data from next connection
1682             getInventoryItemsData(sensorsAsyncResp, inventoryItems,
1683                                   invConnections, objectMgrPaths,
1684                                   std::move(callback), invConnectionsIndex + 1);
1685 
1686             BMCWEB_LOG_DEBUG << "getInventoryItemsData respHandler exit";
1687         };
1688 
1689         // Find DBus object path that implements ObjectManager for the current
1690         // connection.  If no mapping found, default to "/".
1691         auto iter = objectMgrPaths->find(invConnection);
1692         const std::string& objectMgrPath =
1693             (iter != objectMgrPaths->end()) ? iter->second : "/";
1694         BMCWEB_LOG_DEBUG << "ObjectManager path for " << invConnection << " is "
1695                          << objectMgrPath;
1696 
1697         // Get all object paths and their interfaces for current connection
1698         crow::connections::systemBus->async_method_call(
1699             std::move(respHandler), invConnection, objectMgrPath,
1700             "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
1701     }
1702 
1703     BMCWEB_LOG_DEBUG << "getInventoryItemsData exit";
1704 }
1705 
1706 /**
1707  * @brief Gets connections that provide D-Bus data for inventory items.
1708  *
1709  * Gets the D-Bus connections (services) that provide data for the inventory
1710  * items that are associated with sensors.
1711  *
1712  * Finds the connections asynchronously.  Invokes callback when information has
1713  * been obtained.
1714  *
1715  * The callback must have the following signature:
1716  *   @code
1717  *   callback(std::shared_ptr<boost::container::flat_set<std::string>>
1718  *            invConnections)
1719  *   @endcode
1720  *
1721  * @param sensorsAsyncResp Pointer to object holding response data.
1722  * @param inventoryItems D-Bus inventory items associated with sensors.
1723  * @param callback Callback to invoke when connections have been obtained.
1724  */
1725 template <typename Callback>
1726 static void getInventoryItemsConnections(
1727     const std::shared_ptr<SensorsAsyncResp>& sensorsAsyncResp,
1728     const std::shared_ptr<std::vector<InventoryItem>>& inventoryItems,
1729     Callback&& callback)
1730 {
1731     BMCWEB_LOG_DEBUG << "getInventoryItemsConnections enter";
1732 
1733     const std::string path = "/xyz/openbmc_project/inventory";
1734     const std::array<std::string, 4> interfaces = {
1735         "xyz.openbmc_project.Inventory.Item",
1736         "xyz.openbmc_project.Inventory.Item.PowerSupply",
1737         "xyz.openbmc_project.Inventory.Decorator.Asset",
1738         "xyz.openbmc_project.State.Decorator.OperationalStatus"};
1739 
1740     // Response handler for parsing output from GetSubTree
1741     auto respHandler = [callback{std::move(callback)}, sensorsAsyncResp,
1742                         inventoryItems](const boost::system::error_code ec,
1743                                         const GetSubTreeType& subtree) {
1744         BMCWEB_LOG_DEBUG << "getInventoryItemsConnections respHandler enter";
1745         if (ec)
1746         {
1747             messages::internalError(sensorsAsyncResp->res);
1748             BMCWEB_LOG_ERROR
1749                 << "getInventoryItemsConnections respHandler DBus error " << ec;
1750             return;
1751         }
1752 
1753         // Make unique list of connections for desired inventory items
1754         std::shared_ptr<boost::container::flat_set<std::string>>
1755             invConnections =
1756                 std::make_shared<boost::container::flat_set<std::string>>();
1757         invConnections->reserve(8);
1758 
1759         // Loop through objects from GetSubTree
1760         for (const std::pair<
1761                  std::string,
1762                  std::vector<std::pair<std::string, std::vector<std::string>>>>&
1763                  object : subtree)
1764         {
1765             // Check if object path is one of the specified inventory items
1766             const std::string& objPath = object.first;
1767             if (findInventoryItem(inventoryItems, objPath) != nullptr)
1768             {
1769                 // Store all connections to inventory item
1770                 for (const std::pair<std::string, std::vector<std::string>>&
1771                          objData : object.second)
1772                 {
1773                     const std::string& invConnection = objData.first;
1774                     invConnections->insert(invConnection);
1775                 }
1776             }
1777         }
1778 
1779         callback(invConnections);
1780         BMCWEB_LOG_DEBUG << "getInventoryItemsConnections respHandler exit";
1781     };
1782 
1783     // Make call to ObjectMapper to find all inventory items
1784     crow::connections::systemBus->async_method_call(
1785         std::move(respHandler), "xyz.openbmc_project.ObjectMapper",
1786         "/xyz/openbmc_project/object_mapper",
1787         "xyz.openbmc_project.ObjectMapper", "GetSubTree", path, 0, interfaces);
1788     BMCWEB_LOG_DEBUG << "getInventoryItemsConnections exit";
1789 }
1790 
1791 /**
1792  * @brief Gets associations from sensors to inventory items.
1793  *
1794  * Looks for ObjectMapper associations from the specified sensors to related
1795  * inventory items. Then finds the associations from those inventory items to
1796  * their LEDs, if any.
1797  *
1798  * Finds the inventory items asynchronously.  Invokes callback when information
1799  * has been obtained.
1800  *
1801  * The callback must have the following signature:
1802  *   @code
1803  *   callback(std::shared_ptr<std::vector<InventoryItem>> inventoryItems)
1804  *   @endcode
1805  *
1806  * @param sensorsAsyncResp Pointer to object holding response data.
1807  * @param sensorNames All sensors within the current chassis.
1808  * @param objectMgrPaths Mappings from connection name to DBus object path that
1809  * implements ObjectManager.
1810  * @param callback Callback to invoke when inventory items have been obtained.
1811  */
1812 template <typename Callback>
1813 static void getInventoryItemAssociations(
1814     const std::shared_ptr<SensorsAsyncResp>& sensorsAsyncResp,
1815     const std::shared_ptr<boost::container::flat_set<std::string>>& sensorNames,
1816     const std::shared_ptr<boost::container::flat_map<std::string, std::string>>&
1817         objectMgrPaths,
1818     Callback&& callback)
1819 {
1820     BMCWEB_LOG_DEBUG << "getInventoryItemAssociations enter";
1821 
1822     // Response handler for GetManagedObjects
1823     auto respHandler = [callback{std::move(callback)}, sensorsAsyncResp,
1824                         sensorNames](const boost::system::error_code ec,
1825                                      dbus::utility::ManagedObjectType& resp) {
1826         BMCWEB_LOG_DEBUG << "getInventoryItemAssociations respHandler enter";
1827         if (ec)
1828         {
1829             BMCWEB_LOG_ERROR
1830                 << "getInventoryItemAssociations respHandler DBus error " << ec;
1831             messages::internalError(sensorsAsyncResp->res);
1832             return;
1833         }
1834 
1835         // Create vector to hold list of inventory items
1836         std::shared_ptr<std::vector<InventoryItem>> inventoryItems =
1837             std::make_shared<std::vector<InventoryItem>>();
1838 
1839         // Loop through returned object paths
1840         std::string sensorAssocPath;
1841         sensorAssocPath.reserve(128); // avoid memory allocations
1842         for (const auto& objDictEntry : resp)
1843         {
1844             const std::string& objPath =
1845                 static_cast<const std::string&>(objDictEntry.first);
1846             const boost::container::flat_map<
1847                 std::string, boost::container::flat_map<
1848                                  std::string, dbus::utility::DbusVariantType>>&
1849                 interfacesDict = objDictEntry.second;
1850 
1851             // If path is inventory association for one of the specified sensors
1852             for (const std::string& sensorName : *sensorNames)
1853             {
1854                 sensorAssocPath = sensorName;
1855                 sensorAssocPath += "/inventory";
1856                 if (objPath == sensorAssocPath)
1857                 {
1858                     // Get Association interface for object path
1859                     auto assocIt =
1860                         interfacesDict.find("xyz.openbmc_project.Association");
1861                     if (assocIt != interfacesDict.end())
1862                     {
1863                         // Get inventory item from end point
1864                         auto endpointsIt = assocIt->second.find("endpoints");
1865                         if (endpointsIt != assocIt->second.end())
1866                         {
1867                             const std::vector<std::string>* endpoints =
1868                                 std::get_if<std::vector<std::string>>(
1869                                     &endpointsIt->second);
1870                             if ((endpoints != nullptr) && !endpoints->empty())
1871                             {
1872                                 // Add inventory item to vector
1873                                 const std::string& invItemPath =
1874                                     endpoints->front();
1875                                 addInventoryItem(inventoryItems, invItemPath,
1876                                                  sensorName);
1877                             }
1878                         }
1879                     }
1880                     break;
1881                 }
1882             }
1883         }
1884 
1885         // Now loop through the returned object paths again, this time to
1886         // find the leds associated with the inventory items we just found
1887         std::string inventoryAssocPath;
1888         inventoryAssocPath.reserve(128); // avoid memory allocations
1889         for (const auto& objDictEntry : resp)
1890         {
1891             const std::string& objPath =
1892                 static_cast<const std::string&>(objDictEntry.first);
1893             const boost::container::flat_map<
1894                 std::string, boost::container::flat_map<
1895                                  std::string, dbus::utility::DbusVariantType>>&
1896                 interfacesDict = objDictEntry.second;
1897 
1898             for (InventoryItem& inventoryItem : *inventoryItems)
1899             {
1900                 inventoryAssocPath = inventoryItem.objectPath;
1901                 inventoryAssocPath += "/leds";
1902                 if (objPath == inventoryAssocPath)
1903                 {
1904                     // Get Association interface for object path
1905                     auto assocIt =
1906                         interfacesDict.find("xyz.openbmc_project.Association");
1907                     if (assocIt != interfacesDict.end())
1908                     {
1909                         // Get inventory item from end point
1910                         auto endpointsIt = assocIt->second.find("endpoints");
1911                         if (endpointsIt != assocIt->second.end())
1912                         {
1913                             const std::vector<std::string>* endpoints =
1914                                 std::get_if<std::vector<std::string>>(
1915                                     &endpointsIt->second);
1916                             if ((endpoints != nullptr) && !endpoints->empty())
1917                             {
1918                                 // Store LED path in inventory item
1919                                 const std::string& ledPath = endpoints->front();
1920                                 inventoryItem.ledObjectPath = ledPath;
1921                             }
1922                         }
1923                     }
1924                     break;
1925                 }
1926             }
1927         }
1928         callback(inventoryItems);
1929         BMCWEB_LOG_DEBUG << "getInventoryItemAssociations respHandler exit";
1930     };
1931 
1932     // Find DBus object path that implements ObjectManager for ObjectMapper
1933     std::string connection = "xyz.openbmc_project.ObjectMapper";
1934     auto iter = objectMgrPaths->find(connection);
1935     const std::string& objectMgrPath =
1936         (iter != objectMgrPaths->end()) ? iter->second : "/";
1937     BMCWEB_LOG_DEBUG << "ObjectManager path for " << connection << " is "
1938                      << objectMgrPath;
1939 
1940     // Call GetManagedObjects on the ObjectMapper to get all associations
1941     crow::connections::systemBus->async_method_call(
1942         std::move(respHandler), connection, objectMgrPath,
1943         "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
1944 
1945     BMCWEB_LOG_DEBUG << "getInventoryItemAssociations exit";
1946 }
1947 
1948 /**
1949  * @brief Gets D-Bus data for inventory item leds associated with sensors.
1950  *
1951  * Uses the specified connections (services) to obtain D-Bus data for inventory
1952  * item leds associated with sensors.  Stores the resulting data in the
1953  * inventoryItems vector.
1954  *
1955  * This data is later used to provide sensor property values in the JSON
1956  * response.
1957  *
1958  * Finds the inventory item led data asynchronously.  Invokes callback when data
1959  * has been obtained.
1960  *
1961  * The callback must have the following signature:
1962  *   @code
1963  *   callback()
1964  *   @endcode
1965  *
1966  * This function is called recursively, obtaining data asynchronously from one
1967  * connection in each call.  This ensures the callback is not invoked until the
1968  * last asynchronous function has completed.
1969  *
1970  * @param sensorsAsyncResp Pointer to object holding response data.
1971  * @param inventoryItems D-Bus inventory items associated with sensors.
1972  * @param ledConnections Connections that provide data for the inventory leds.
1973  * @param callback Callback to invoke when inventory data has been obtained.
1974  * @param ledConnectionsIndex Current index in ledConnections.  Only specified
1975  * in recursive calls to this function.
1976  */
1977 template <typename Callback>
1978 void getInventoryLedData(
1979     std::shared_ptr<SensorsAsyncResp> sensorsAsyncResp,
1980     std::shared_ptr<std::vector<InventoryItem>> inventoryItems,
1981     std::shared_ptr<boost::container::flat_map<std::string, std::string>>
1982         ledConnections,
1983     Callback&& callback, size_t ledConnectionsIndex = 0)
1984 {
1985     BMCWEB_LOG_DEBUG << "getInventoryLedData enter";
1986 
1987     // If no more connections left, call callback
1988     if (ledConnectionsIndex >= ledConnections->size())
1989     {
1990         callback();
1991         BMCWEB_LOG_DEBUG << "getInventoryLedData exit";
1992         return;
1993     }
1994 
1995     // Get inventory item data from current connection
1996     auto it = ledConnections->nth(ledConnectionsIndex);
1997     if (it != ledConnections->end())
1998     {
1999         const std::string& ledPath = (*it).first;
2000         const std::string& ledConnection = (*it).second;
2001         // Response handler for Get State property
2002         auto respHandler =
2003             [sensorsAsyncResp, inventoryItems, ledConnections, ledPath,
2004              callback{std::move(callback)},
2005              ledConnectionsIndex](const boost::system::error_code ec,
2006                                   const std::variant<std::string>& ledState) {
2007                 BMCWEB_LOG_DEBUG << "getInventoryLedData respHandler enter";
2008                 if (ec)
2009                 {
2010                     BMCWEB_LOG_ERROR
2011                         << "getInventoryLedData respHandler DBus error " << ec;
2012                     messages::internalError(sensorsAsyncResp->res);
2013                     return;
2014                 }
2015 
2016                 const std::string* state = std::get_if<std::string>(&ledState);
2017                 if (state != nullptr)
2018                 {
2019                     BMCWEB_LOG_DEBUG << "Led state: " << *state;
2020                     // Find inventory item with this LED object path
2021                     InventoryItem* inventoryItem =
2022                         findInventoryItemForLed(*inventoryItems, ledPath);
2023                     if (inventoryItem != nullptr)
2024                     {
2025                         // Store LED state in InventoryItem
2026                         if (boost::ends_with(*state, "On"))
2027                         {
2028                             inventoryItem->ledState = LedState::ON;
2029                         }
2030                         else if (boost::ends_with(*state, "Blink"))
2031                         {
2032                             inventoryItem->ledState = LedState::BLINK;
2033                         }
2034                         else if (boost::ends_with(*state, "Off"))
2035                         {
2036                             inventoryItem->ledState = LedState::OFF;
2037                         }
2038                         else
2039                         {
2040                             inventoryItem->ledState = LedState::UNKNOWN;
2041                         }
2042                     }
2043                 }
2044                 else
2045                 {
2046                     BMCWEB_LOG_DEBUG << "Failed to find State data for LED: "
2047                                      << ledPath;
2048                 }
2049 
2050                 // Recurse to get LED data from next connection
2051                 getInventoryLedData(sensorsAsyncResp, inventoryItems,
2052                                     ledConnections, std::move(callback),
2053                                     ledConnectionsIndex + 1);
2054 
2055                 BMCWEB_LOG_DEBUG << "getInventoryLedData respHandler exit";
2056             };
2057 
2058         // Get the State property for the current LED
2059         crow::connections::systemBus->async_method_call(
2060             std::move(respHandler), ledConnection, ledPath,
2061             "org.freedesktop.DBus.Properties", "Get",
2062             "xyz.openbmc_project.Led.Physical", "State");
2063     }
2064 
2065     BMCWEB_LOG_DEBUG << "getInventoryLedData exit";
2066 }
2067 
2068 /**
2069  * @brief Gets LED data for LEDs associated with given inventory items.
2070  *
2071  * Gets the D-Bus connections (services) that provide LED data for the LEDs
2072  * associated with the specified inventory items.  Then gets the LED data from
2073  * each connection and stores it in the inventory item.
2074  *
2075  * This data is later used to provide sensor property values in the JSON
2076  * response.
2077  *
2078  * Finds the LED data asynchronously.  Invokes callback when information has
2079  * been obtained.
2080  *
2081  * The callback must have the following signature:
2082  *   @code
2083  *   callback()
2084  *   @endcode
2085  *
2086  * @param sensorsAsyncResp Pointer to object holding response data.
2087  * @param inventoryItems D-Bus inventory items associated with sensors.
2088  * @param callback Callback to invoke when inventory items have been obtained.
2089  */
2090 template <typename Callback>
2091 void getInventoryLeds(
2092     std::shared_ptr<SensorsAsyncResp> sensorsAsyncResp,
2093     std::shared_ptr<std::vector<InventoryItem>> inventoryItems,
2094     Callback&& callback)
2095 {
2096     BMCWEB_LOG_DEBUG << "getInventoryLeds enter";
2097 
2098     const std::string path = "/xyz/openbmc_project";
2099     const std::array<std::string, 1> interfaces = {
2100         "xyz.openbmc_project.Led.Physical"};
2101 
2102     // Response handler for parsing output from GetSubTree
2103     auto respHandler = [callback{std::move(callback)}, sensorsAsyncResp,
2104                         inventoryItems](const boost::system::error_code ec,
2105                                         const GetSubTreeType& subtree) {
2106         BMCWEB_LOG_DEBUG << "getInventoryLeds respHandler enter";
2107         if (ec)
2108         {
2109             messages::internalError(sensorsAsyncResp->res);
2110             BMCWEB_LOG_ERROR << "getInventoryLeds respHandler DBus error "
2111                              << ec;
2112             return;
2113         }
2114 
2115         // Build map of LED object paths to connections
2116         std::shared_ptr<boost::container::flat_map<std::string, std::string>>
2117             ledConnections = std::make_shared<
2118                 boost::container::flat_map<std::string, std::string>>();
2119 
2120         // Loop through objects from GetSubTree
2121         for (const std::pair<
2122                  std::string,
2123                  std::vector<std::pair<std::string, std::vector<std::string>>>>&
2124                  object : subtree)
2125         {
2126             // Check if object path is LED for one of the specified inventory
2127             // items
2128             const std::string& ledPath = object.first;
2129             if (findInventoryItemForLed(*inventoryItems, ledPath) != nullptr)
2130             {
2131                 // Add mapping from ledPath to connection
2132                 const std::string& connection = object.second.begin()->first;
2133                 (*ledConnections)[ledPath] = connection;
2134                 BMCWEB_LOG_DEBUG << "Added mapping " << ledPath << " -> "
2135                                  << connection;
2136             }
2137         }
2138 
2139         getInventoryLedData(sensorsAsyncResp, inventoryItems, ledConnections,
2140                             std::move(callback));
2141         BMCWEB_LOG_DEBUG << "getInventoryLeds respHandler exit";
2142     };
2143     // Make call to ObjectMapper to find all inventory items
2144     crow::connections::systemBus->async_method_call(
2145         std::move(respHandler), "xyz.openbmc_project.ObjectMapper",
2146         "/xyz/openbmc_project/object_mapper",
2147         "xyz.openbmc_project.ObjectMapper", "GetSubTree", path, 0, interfaces);
2148     BMCWEB_LOG_DEBUG << "getInventoryLeds exit";
2149 }
2150 
2151 /**
2152  * @brief Gets D-Bus data for Power Supply Attributes such as EfficiencyPercent
2153  *
2154  * Uses the specified connections (services) (currently assumes just one) to
2155  * obtain D-Bus data for Power Supply Attributes. Stores the resulting data in
2156  * the inventoryItems vector. Only stores data in Power Supply inventoryItems.
2157  *
2158  * This data is later used to provide sensor property values in the JSON
2159  * response.
2160  *
2161  * Finds the Power Supply Attributes data asynchronously.  Invokes callback
2162  * when data has been obtained.
2163  *
2164  * The callback must have the following signature:
2165  *   @code
2166  *   callback(std::shared_ptr<std::vector<InventoryItem>> inventoryItems)
2167  *   @endcode
2168  *
2169  * @param sensorsAsyncResp Pointer to object holding response data.
2170  * @param inventoryItems D-Bus inventory items associated with sensors.
2171  * @param psAttributesConnections Connections that provide data for the Power
2172  *        Supply Attributes
2173  * @param callback Callback to invoke when data has been obtained.
2174  */
2175 template <typename Callback>
2176 void getPowerSupplyAttributesData(
2177     const std::shared_ptr<SensorsAsyncResp>& sensorsAsyncResp,
2178     std::shared_ptr<std::vector<InventoryItem>> inventoryItems,
2179     const boost::container::flat_map<std::string, std::string>&
2180         psAttributesConnections,
2181     Callback&& callback)
2182 {
2183     BMCWEB_LOG_DEBUG << "getPowerSupplyAttributesData enter";
2184 
2185     if (psAttributesConnections.empty())
2186     {
2187         BMCWEB_LOG_DEBUG << "Can't find PowerSupplyAttributes, no connections!";
2188         callback(inventoryItems);
2189         return;
2190     }
2191 
2192     // Assuming just one connection (service) for now
2193     auto it = psAttributesConnections.nth(0);
2194 
2195     const std::string& psAttributesPath = (*it).first;
2196     const std::string& psAttributesConnection = (*it).second;
2197 
2198     // Response handler for Get DeratingFactor property
2199     auto respHandler = [sensorsAsyncResp, inventoryItems,
2200                         callback{std::move(callback)}](
2201                            const boost::system::error_code ec,
2202                            const std::variant<uint32_t>& deratingFactor) {
2203         BMCWEB_LOG_DEBUG << "getPowerSupplyAttributesData respHandler enter";
2204         if (ec)
2205         {
2206             BMCWEB_LOG_ERROR
2207                 << "getPowerSupplyAttributesData respHandler DBus error " << ec;
2208             messages::internalError(sensorsAsyncResp->res);
2209             return;
2210         }
2211 
2212         const uint32_t* value = std::get_if<uint32_t>(&deratingFactor);
2213         if (value != nullptr)
2214         {
2215             BMCWEB_LOG_DEBUG << "PS EfficiencyPercent value: " << *value;
2216             // Store value in Power Supply Inventory Items
2217             for (InventoryItem& inventoryItem : *inventoryItems)
2218             {
2219                 if (inventoryItem.isPowerSupply == true)
2220                 {
2221                     inventoryItem.powerSupplyEfficiencyPercent =
2222                         static_cast<int>(*value);
2223                 }
2224             }
2225         }
2226         else
2227         {
2228             BMCWEB_LOG_DEBUG
2229                 << "Failed to find EfficiencyPercent value for PowerSupplies";
2230         }
2231 
2232         BMCWEB_LOG_DEBUG << "getPowerSupplyAttributesData respHandler exit";
2233         callback(inventoryItems);
2234     };
2235 
2236     // Get the DeratingFactor property for the PowerSupplyAttributes
2237     // Currently only property on the interface/only one we care about
2238     crow::connections::systemBus->async_method_call(
2239         std::move(respHandler), psAttributesConnection, psAttributesPath,
2240         "org.freedesktop.DBus.Properties", "Get",
2241         "xyz.openbmc_project.Control.PowerSupplyAttributes", "DeratingFactor");
2242 
2243     BMCWEB_LOG_DEBUG << "getPowerSupplyAttributesData exit";
2244 }
2245 
2246 /**
2247  * @brief Gets the Power Supply Attributes such as EfficiencyPercent
2248  *
2249  * Gets the D-Bus connection (service) that provides Power Supply Attributes
2250  * data. Then gets the Power Supply Attributes data from the connection
2251  * (currently just assumes 1 connection) and stores the data in the inventory
2252  * item.
2253  *
2254  * This data is later used to provide sensor property values in the JSON
2255  * response. DeratingFactor on D-Bus is mapped to EfficiencyPercent on Redfish.
2256  *
2257  * Finds the Power Supply Attributes data asynchronously. Invokes callback
2258  * when information has been obtained.
2259  *
2260  * The callback must have the following signature:
2261  *   @code
2262  *   callback(std::shared_ptr<std::vector<InventoryItem>> inventoryItems)
2263  *   @endcode
2264  *
2265  * @param sensorsAsyncResp Pointer to object holding response data.
2266  * @param inventoryItems D-Bus inventory items associated with sensors.
2267  * @param callback Callback to invoke when data has been obtained.
2268  */
2269 template <typename Callback>
2270 void getPowerSupplyAttributes(
2271     std::shared_ptr<SensorsAsyncResp> sensorsAsyncResp,
2272     std::shared_ptr<std::vector<InventoryItem>> inventoryItems,
2273     Callback&& callback)
2274 {
2275     BMCWEB_LOG_DEBUG << "getPowerSupplyAttributes enter";
2276 
2277     // Only need the power supply attributes when the Power Schema
2278     if (sensorsAsyncResp->chassisSubNode != sensors::node::power)
2279     {
2280         BMCWEB_LOG_DEBUG << "getPowerSupplyAttributes exit since not Power";
2281         callback(inventoryItems);
2282         return;
2283     }
2284 
2285     const std::array<std::string, 1> interfaces = {
2286         "xyz.openbmc_project.Control.PowerSupplyAttributes"};
2287 
2288     // Response handler for parsing output from GetSubTree
2289     auto respHandler = [callback{std::move(callback)}, sensorsAsyncResp,
2290                         inventoryItems](const boost::system::error_code ec,
2291                                         const GetSubTreeType& subtree) {
2292         BMCWEB_LOG_DEBUG << "getPowerSupplyAttributes respHandler enter";
2293         if (ec)
2294         {
2295             messages::internalError(sensorsAsyncResp->res);
2296             BMCWEB_LOG_ERROR
2297                 << "getPowerSupplyAttributes respHandler DBus error " << ec;
2298             return;
2299         }
2300         if (subtree.size() == 0)
2301         {
2302             BMCWEB_LOG_DEBUG << "Can't find Power Supply Attributes!";
2303             callback(inventoryItems);
2304             return;
2305         }
2306 
2307         // Currently we only support 1 power supply attribute, use this for
2308         // all the power supplies. Build map of object path to connection.
2309         // Assume just 1 connection and 1 path for now.
2310         boost::container::flat_map<std::string, std::string>
2311             psAttributesConnections;
2312 
2313         if (subtree[0].first.empty() || subtree[0].second.empty())
2314         {
2315             BMCWEB_LOG_DEBUG << "Power Supply Attributes mapper error!";
2316             callback(inventoryItems);
2317             return;
2318         }
2319 
2320         const std::string& psAttributesPath = subtree[0].first;
2321         const std::string& connection = subtree[0].second.begin()->first;
2322 
2323         if (connection.empty())
2324         {
2325             BMCWEB_LOG_DEBUG << "Power Supply Attributes mapper error!";
2326             callback(inventoryItems);
2327             return;
2328         }
2329 
2330         psAttributesConnections[psAttributesPath] = connection;
2331         BMCWEB_LOG_DEBUG << "Added mapping " << psAttributesPath << " -> "
2332                          << connection;
2333 
2334         getPowerSupplyAttributesData(sensorsAsyncResp, inventoryItems,
2335                                      psAttributesConnections,
2336                                      std::move(callback));
2337         BMCWEB_LOG_DEBUG << "getPowerSupplyAttributes respHandler exit";
2338     };
2339     // Make call to ObjectMapper to find the PowerSupplyAttributes service
2340     crow::connections::systemBus->async_method_call(
2341         std::move(respHandler), "xyz.openbmc_project.ObjectMapper",
2342         "/xyz/openbmc_project/object_mapper",
2343         "xyz.openbmc_project.ObjectMapper", "GetSubTree",
2344         "/xyz/openbmc_project", 0, interfaces);
2345     BMCWEB_LOG_DEBUG << "getPowerSupplyAttributes exit";
2346 }
2347 
2348 /**
2349  * @brief Gets inventory items associated with sensors.
2350  *
2351  * Finds the inventory items that are associated with the specified sensors.
2352  * Then gets D-Bus data for the inventory items, such as presence and VPD.
2353  *
2354  * This data is later used to provide sensor property values in the JSON
2355  * response.
2356  *
2357  * Finds the inventory items asynchronously.  Invokes callback when the
2358  * inventory items have been obtained.
2359  *
2360  * The callback must have the following signature:
2361  *   @code
2362  *   callback(std::shared_ptr<std::vector<InventoryItem>> inventoryItems)
2363  *   @endcode
2364  *
2365  * @param sensorsAsyncResp Pointer to object holding response data.
2366  * @param sensorNames All sensors within the current chassis.
2367  * @param objectMgrPaths Mappings from connection name to DBus object path that
2368  * implements ObjectManager.
2369  * @param callback Callback to invoke when inventory items have been obtained.
2370  */
2371 template <typename Callback>
2372 static void getInventoryItems(
2373     std::shared_ptr<SensorsAsyncResp> sensorsAsyncResp,
2374     const std::shared_ptr<boost::container::flat_set<std::string>> sensorNames,
2375     std::shared_ptr<boost::container::flat_map<std::string, std::string>>
2376         objectMgrPaths,
2377     Callback&& callback)
2378 {
2379     BMCWEB_LOG_DEBUG << "getInventoryItems enter";
2380     auto getInventoryItemAssociationsCb =
2381         [sensorsAsyncResp, objectMgrPaths, callback{std::move(callback)}](
2382             std::shared_ptr<std::vector<InventoryItem>> inventoryItems) {
2383             BMCWEB_LOG_DEBUG << "getInventoryItemAssociationsCb enter";
2384             auto getInventoryItemsConnectionsCb =
2385                 [sensorsAsyncResp, inventoryItems, objectMgrPaths,
2386                  callback{std::move(callback)}](
2387                     std::shared_ptr<boost::container::flat_set<std::string>>
2388                         invConnections) {
2389                     BMCWEB_LOG_DEBUG << "getInventoryItemsConnectionsCb enter";
2390                     auto getInventoryItemsDataCb =
2391                         [sensorsAsyncResp, inventoryItems,
2392                          callback{std::move(callback)}]() {
2393                             BMCWEB_LOG_DEBUG << "getInventoryItemsDataCb enter";
2394 
2395                             auto getInventoryLedsCb = [sensorsAsyncResp,
2396                                                        inventoryItems,
2397                                                        callback{std::move(
2398                                                            callback)}]() {
2399                                 BMCWEB_LOG_DEBUG << "getInventoryLedsCb enter";
2400                                 // Find Power Supply Attributes and get the data
2401                                 getPowerSupplyAttributes(sensorsAsyncResp,
2402                                                          inventoryItems,
2403                                                          std::move(callback));
2404                                 BMCWEB_LOG_DEBUG << "getInventoryLedsCb exit";
2405                             };
2406 
2407                             // Find led connections and get the data
2408                             getInventoryLeds(sensorsAsyncResp, inventoryItems,
2409                                              std::move(getInventoryLedsCb));
2410                             BMCWEB_LOG_DEBUG << "getInventoryItemsDataCb exit";
2411                         };
2412 
2413                     // Get inventory item data from connections
2414                     getInventoryItemsData(sensorsAsyncResp, inventoryItems,
2415                                           invConnections, objectMgrPaths,
2416                                           std::move(getInventoryItemsDataCb));
2417                     BMCWEB_LOG_DEBUG << "getInventoryItemsConnectionsCb exit";
2418                 };
2419 
2420             // Get connections that provide inventory item data
2421             getInventoryItemsConnections(
2422                 sensorsAsyncResp, inventoryItems,
2423                 std::move(getInventoryItemsConnectionsCb));
2424             BMCWEB_LOG_DEBUG << "getInventoryItemAssociationsCb exit";
2425         };
2426 
2427     // Get associations from sensors to inventory items
2428     getInventoryItemAssociations(sensorsAsyncResp, sensorNames, objectMgrPaths,
2429                                  std::move(getInventoryItemAssociationsCb));
2430     BMCWEB_LOG_DEBUG << "getInventoryItems exit";
2431 }
2432 
2433 /**
2434  * @brief Returns JSON PowerSupply object for the specified inventory item.
2435  *
2436  * Searches for a JSON PowerSupply object that matches the specified inventory
2437  * item.  If one is not found, a new PowerSupply object is added to the JSON
2438  * array.
2439  *
2440  * Multiple sensors are often associated with one power supply inventory item.
2441  * As a result, multiple sensor values are stored in one JSON PowerSupply
2442  * object.
2443  *
2444  * @param powerSupplyArray JSON array containing Redfish PowerSupply objects.
2445  * @param inventoryItem Inventory item for the power supply.
2446  * @param chassisId Chassis that contains the power supply.
2447  * @return JSON PowerSupply object for the specified inventory item.
2448  */
2449 inline nlohmann::json& getPowerSupply(nlohmann::json& powerSupplyArray,
2450                                       const InventoryItem& inventoryItem,
2451                                       const std::string& chassisId)
2452 {
2453     // Check if matching PowerSupply object already exists in JSON array
2454     for (nlohmann::json& powerSupply : powerSupplyArray)
2455     {
2456         if (powerSupply["MemberId"] == inventoryItem.name)
2457         {
2458             return powerSupply;
2459         }
2460     }
2461 
2462     // Add new PowerSupply object to JSON array
2463     powerSupplyArray.push_back({});
2464     nlohmann::json& powerSupply = powerSupplyArray.back();
2465     powerSupply["@odata.id"] =
2466         "/redfish/v1/Chassis/" + chassisId + "/Power#/PowerSupplies/";
2467     powerSupply["MemberId"] = inventoryItem.name;
2468     powerSupply["Name"] = boost::replace_all_copy(inventoryItem.name, "_", " ");
2469     powerSupply["Manufacturer"] = inventoryItem.manufacturer;
2470     powerSupply["Model"] = inventoryItem.model;
2471     powerSupply["PartNumber"] = inventoryItem.partNumber;
2472     powerSupply["SerialNumber"] = inventoryItem.serialNumber;
2473     setLedState(powerSupply, &inventoryItem);
2474 
2475     if (inventoryItem.powerSupplyEfficiencyPercent >= 0)
2476     {
2477         powerSupply["EfficiencyPercent"] =
2478             inventoryItem.powerSupplyEfficiencyPercent;
2479     }
2480 
2481     powerSupply["Status"]["State"] = getState(&inventoryItem);
2482     const char* health = inventoryItem.isFunctional ? "OK" : "Critical";
2483     powerSupply["Status"]["Health"] = health;
2484 
2485     return powerSupply;
2486 }
2487 
2488 /**
2489  * @brief Gets the values of the specified sensors.
2490  *
2491  * Stores the results as JSON in the SensorsAsyncResp.
2492  *
2493  * Gets the sensor values asynchronously.  Stores the results later when the
2494  * information has been obtained.
2495  *
2496  * The sensorNames set contains all requested sensors for the current chassis.
2497  *
2498  * To minimize the number of DBus calls, the DBus method
2499  * org.freedesktop.DBus.ObjectManager.GetManagedObjects() is used to get the
2500  * values of all sensors provided by a connection (service).
2501  *
2502  * The connections set contains all the connections that provide sensor values.
2503  *
2504  * The objectMgrPaths map contains mappings from a connection name to the
2505  * corresponding DBus object path that implements ObjectManager.
2506  *
2507  * The InventoryItem vector contains D-Bus inventory items associated with the
2508  * sensors.  Inventory item data is needed for some Redfish sensor properties.
2509  *
2510  * @param SensorsAsyncResp Pointer to object holding response data.
2511  * @param sensorNames All requested sensors within the current chassis.
2512  * @param connections Connections that provide sensor values.
2513  * @param objectMgrPaths Mappings from connection name to DBus object path that
2514  * implements ObjectManager.
2515  * @param inventoryItems Inventory items associated with the sensors.
2516  */
2517 inline void getSensorData(
2518     const std::shared_ptr<SensorsAsyncResp>& sensorsAsyncResp,
2519     const std::shared_ptr<boost::container::flat_set<std::string>>& sensorNames,
2520     const boost::container::flat_set<std::string>& connections,
2521     const std::shared_ptr<boost::container::flat_map<std::string, std::string>>&
2522         objectMgrPaths,
2523     const std::shared_ptr<std::vector<InventoryItem>>& inventoryItems)
2524 {
2525     BMCWEB_LOG_DEBUG << "getSensorData enter";
2526     // Get managed objects from all services exposing sensors
2527     for (const std::string& connection : connections)
2528     {
2529         // Response handler to process managed objects
2530         auto getManagedObjectsCb = [sensorsAsyncResp, sensorNames,
2531                                     inventoryItems](
2532                                        const boost::system::error_code ec,
2533                                        ManagedObjectsVectorType& resp) {
2534             BMCWEB_LOG_DEBUG << "getManagedObjectsCb enter";
2535             if (ec)
2536             {
2537                 BMCWEB_LOG_ERROR << "getManagedObjectsCb DBUS error: " << ec;
2538                 messages::internalError(sensorsAsyncResp->res);
2539                 return;
2540             }
2541             // Go through all objects and update response with sensor data
2542             for (const auto& objDictEntry : resp)
2543             {
2544                 const std::string& objPath =
2545                     static_cast<const std::string&>(objDictEntry.first);
2546                 BMCWEB_LOG_DEBUG << "getManagedObjectsCb parsing object "
2547                                  << objPath;
2548 
2549                 std::vector<std::string> split;
2550                 // Reserve space for
2551                 // /xyz/openbmc_project/sensors/<name>/<subname>
2552                 split.reserve(6);
2553                 boost::algorithm::split(split, objPath, boost::is_any_of("/"));
2554                 if (split.size() < 6)
2555                 {
2556                     BMCWEB_LOG_ERROR << "Got path that isn't long enough "
2557                                      << objPath;
2558                     continue;
2559                 }
2560                 // These indexes aren't intuitive, as boost::split puts an empty
2561                 // string at the beginning
2562                 const std::string& sensorType = split[4];
2563                 const std::string& sensorName = split[5];
2564                 BMCWEB_LOG_DEBUG << "sensorName " << sensorName
2565                                  << " sensorType " << sensorType;
2566                 if (sensorNames->find(objPath) == sensorNames->end())
2567                 {
2568                     BMCWEB_LOG_ERROR << sensorName << " not in sensor list ";
2569                     continue;
2570                 }
2571 
2572                 // Find inventory item (if any) associated with sensor
2573                 InventoryItem* inventoryItem =
2574                     findInventoryItemForSensor(inventoryItems, objPath);
2575 
2576                 const std::string& sensorSchema =
2577                     sensorsAsyncResp->chassisSubNode;
2578 
2579                 nlohmann::json* sensorJson = nullptr;
2580 
2581                 if (sensorSchema == sensors::node::sensors)
2582                 {
2583                     sensorsAsyncResp->res.jsonValue["@odata.id"] =
2584                         "/redfish/v1/Chassis/" + sensorsAsyncResp->chassisId +
2585                         "/" + sensorsAsyncResp->chassisSubNode + "/" +
2586                         sensorName;
2587                     sensorJson = &(sensorsAsyncResp->res.jsonValue);
2588                 }
2589                 else
2590                 {
2591                     std::string fieldName;
2592                     if (sensorType == "temperature")
2593                     {
2594                         fieldName = "Temperatures";
2595                     }
2596                     else if (sensorType == "fan" || sensorType == "fan_tach" ||
2597                              sensorType == "fan_pwm")
2598                     {
2599                         fieldName = "Fans";
2600                     }
2601                     else if (sensorType == "voltage")
2602                     {
2603                         fieldName = "Voltages";
2604                     }
2605                     else if (sensorType == "power")
2606                     {
2607                         if (!sensorName.compare("total_power"))
2608                         {
2609                             fieldName = "PowerControl";
2610                         }
2611                         else if ((inventoryItem != nullptr) &&
2612                                  (inventoryItem->isPowerSupply))
2613                         {
2614                             fieldName = "PowerSupplies";
2615                         }
2616                         else
2617                         {
2618                             // Other power sensors are in SensorCollection
2619                             continue;
2620                         }
2621                     }
2622                     else
2623                     {
2624                         BMCWEB_LOG_ERROR << "Unsure how to handle sensorType "
2625                                          << sensorType;
2626                         continue;
2627                     }
2628 
2629                     nlohmann::json& tempArray =
2630                         sensorsAsyncResp->res.jsonValue[fieldName];
2631                     if (fieldName == "PowerControl")
2632                     {
2633                         if (tempArray.empty())
2634                         {
2635                             // Put multiple "sensors" into a single
2636                             // PowerControl. Follows MemberId naming and
2637                             // naming in power.hpp.
2638                             tempArray.push_back(
2639                                 {{"@odata.id",
2640                                   "/redfish/v1/Chassis/" +
2641                                       sensorsAsyncResp->chassisId + "/" +
2642                                       sensorsAsyncResp->chassisSubNode + "#/" +
2643                                       fieldName + "/0"}});
2644                         }
2645                         sensorJson = &(tempArray.back());
2646                     }
2647                     else if (fieldName == "PowerSupplies")
2648                     {
2649                         if (inventoryItem != nullptr)
2650                         {
2651                             sensorJson =
2652                                 &(getPowerSupply(tempArray, *inventoryItem,
2653                                                  sensorsAsyncResp->chassisId));
2654                         }
2655                     }
2656                     else
2657                     {
2658                         tempArray.push_back(
2659                             {{"@odata.id",
2660                               "/redfish/v1/Chassis/" +
2661                                   sensorsAsyncResp->chassisId + "/" +
2662                                   sensorsAsyncResp->chassisSubNode + "#/" +
2663                                   fieldName + "/"}});
2664                         sensorJson = &(tempArray.back());
2665                     }
2666                 }
2667 
2668                 if (sensorJson != nullptr)
2669                 {
2670                     objectInterfacesToJson(
2671                         sensorName, sensorType, sensorsAsyncResp,
2672                         objDictEntry.second, *sensorJson, inventoryItem);
2673                 }
2674             }
2675             if (sensorsAsyncResp.use_count() == 1)
2676             {
2677                 sortJSONResponse(sensorsAsyncResp);
2678                 if (sensorsAsyncResp->chassisSubNode == sensors::node::thermal)
2679                 {
2680                     populateFanRedundancy(sensorsAsyncResp);
2681                 }
2682             }
2683             BMCWEB_LOG_DEBUG << "getManagedObjectsCb exit";
2684         };
2685 
2686         // Find DBus object path that implements ObjectManager for the current
2687         // connection.  If no mapping found, default to "/".
2688         auto iter = objectMgrPaths->find(connection);
2689         const std::string& objectMgrPath =
2690             (iter != objectMgrPaths->end()) ? iter->second : "/";
2691         BMCWEB_LOG_DEBUG << "ObjectManager path for " << connection << " is "
2692                          << objectMgrPath;
2693 
2694         crow::connections::systemBus->async_method_call(
2695             getManagedObjectsCb, connection, objectMgrPath,
2696             "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
2697     }
2698     BMCWEB_LOG_DEBUG << "getSensorData exit";
2699 }
2700 
2701 inline void processSensorList(
2702     const std::shared_ptr<SensorsAsyncResp>& sensorsAsyncResp,
2703     const std::shared_ptr<boost::container::flat_set<std::string>>& sensorNames)
2704 {
2705     auto getConnectionCb =
2706         [sensorsAsyncResp, sensorNames](
2707             const boost::container::flat_set<std::string>& connections) {
2708             BMCWEB_LOG_DEBUG << "getConnectionCb enter";
2709             auto getObjectManagerPathsCb =
2710                 [sensorsAsyncResp, sensorNames,
2711                  connections](const std::shared_ptr<boost::container::flat_map<
2712                                   std::string, std::string>>& objectMgrPaths) {
2713                     BMCWEB_LOG_DEBUG << "getObjectManagerPathsCb enter";
2714                     auto getInventoryItemsCb =
2715                         [sensorsAsyncResp, sensorNames, connections,
2716                          objectMgrPaths](
2717                             const std::shared_ptr<std::vector<InventoryItem>>&
2718                                 inventoryItems) {
2719                             BMCWEB_LOG_DEBUG << "getInventoryItemsCb enter";
2720                             // Get sensor data and store results in JSON
2721                             getSensorData(sensorsAsyncResp, sensorNames,
2722                                           connections, objectMgrPaths,
2723                                           inventoryItems);
2724                             BMCWEB_LOG_DEBUG << "getInventoryItemsCb exit";
2725                         };
2726 
2727                     // Get inventory items associated with sensors
2728                     getInventoryItems(sensorsAsyncResp, sensorNames,
2729                                       objectMgrPaths,
2730                                       std::move(getInventoryItemsCb));
2731 
2732                     BMCWEB_LOG_DEBUG << "getObjectManagerPathsCb exit";
2733                 };
2734 
2735             // Get mapping from connection names to the DBus object
2736             // paths that implement the ObjectManager interface
2737             getObjectManagerPaths(sensorsAsyncResp,
2738                                   std::move(getObjectManagerPathsCb));
2739             BMCWEB_LOG_DEBUG << "getConnectionCb exit";
2740         };
2741 
2742     // Get set of connections that provide sensor values
2743     getConnections(sensorsAsyncResp, sensorNames, std::move(getConnectionCb));
2744 }
2745 
2746 /**
2747  * @brief Entry point for retrieving sensors data related to requested
2748  *        chassis.
2749  * @param SensorsAsyncResp   Pointer to object holding response data
2750  */
2751 inline void
2752     getChassisData(const std::shared_ptr<SensorsAsyncResp>& sensorsAsyncResp)
2753 {
2754     BMCWEB_LOG_DEBUG << "getChassisData enter";
2755     auto getChassisCb =
2756         [sensorsAsyncResp](
2757             const std::shared_ptr<boost::container::flat_set<std::string>>&
2758                 sensorNames) {
2759             BMCWEB_LOG_DEBUG << "getChassisCb enter";
2760             processSensorList(sensorsAsyncResp, sensorNames);
2761             BMCWEB_LOG_DEBUG << "getChassisCb exit";
2762         };
2763     sensorsAsyncResp->res.jsonValue["Redundancy"] = nlohmann::json::array();
2764 
2765     // Get set of sensors in chassis
2766     getChassis(sensorsAsyncResp, std::move(getChassisCb));
2767     BMCWEB_LOG_DEBUG << "getChassisData exit";
2768 }
2769 
2770 /**
2771  * @brief Find the requested sensorName in the list of all sensors supplied by
2772  * the chassis node
2773  *
2774  * @param sensorName   The sensor name supplied in the PATCH request
2775  * @param sensorsList  The list of sensors managed by the chassis node
2776  * @param sensorsModified  The list of sensors that were found as a result of
2777  *                         repeated calls to this function
2778  */
2779 inline bool findSensorNameUsingSensorPath(
2780     std::string_view sensorName,
2781     boost::container::flat_set<std::string>& sensorsList,
2782     boost::container::flat_set<std::string>& sensorsModified)
2783 {
2784     for (auto& chassisSensor : sensorsList)
2785     {
2786         sdbusplus::message::object_path path(chassisSensor);
2787         std::string thisSensorName = path.filename();
2788         if (thisSensorName.empty())
2789         {
2790             continue;
2791         }
2792         if (thisSensorName == sensorName)
2793         {
2794             sensorsModified.emplace(chassisSensor);
2795             return true;
2796         }
2797     }
2798     return false;
2799 }
2800 
2801 /**
2802  * @brief Entry point for overriding sensor values of given sensor
2803  *
2804  * @param res   response object
2805  * @param allCollections   Collections extract from sensors' request patch info
2806  * @param chassisSubNode   Chassis Node for which the query has to happen
2807  */
2808 inline void setSensorsOverride(
2809     const std::shared_ptr<SensorsAsyncResp>& sensorAsyncResp,
2810     std::unordered_map<std::string, std::vector<nlohmann::json>>&
2811         allCollections)
2812 {
2813     BMCWEB_LOG_INFO << "setSensorsOverride for subNode"
2814                     << sensorAsyncResp->chassisSubNode << "\n";
2815 
2816     const char* propertyValueName;
2817     std::unordered_map<std::string, std::pair<double, std::string>> overrideMap;
2818     std::string memberId;
2819     double value;
2820     for (auto& collectionItems : allCollections)
2821     {
2822         if (collectionItems.first == "Temperatures")
2823         {
2824             propertyValueName = "ReadingCelsius";
2825         }
2826         else if (collectionItems.first == "Fans")
2827         {
2828             propertyValueName = "Reading";
2829         }
2830         else
2831         {
2832             propertyValueName = "ReadingVolts";
2833         }
2834         for (auto& item : collectionItems.second)
2835         {
2836             if (!json_util::readJson(item, sensorAsyncResp->res, "MemberId",
2837                                      memberId, propertyValueName, value))
2838             {
2839                 return;
2840             }
2841             overrideMap.emplace(memberId,
2842                                 std::make_pair(value, collectionItems.first));
2843         }
2844     }
2845 
2846     auto getChassisSensorListCb = [sensorAsyncResp, overrideMap](
2847                                       const std::shared_ptr<
2848                                           boost::container::flat_set<
2849                                               std::string>>& sensorsList) {
2850         // Match sensor names in the PATCH request to those managed by the
2851         // chassis node
2852         const std::shared_ptr<boost::container::flat_set<std::string>>
2853             sensorNames =
2854                 std::make_shared<boost::container::flat_set<std::string>>();
2855         for (const auto& item : overrideMap)
2856         {
2857             const auto& sensor = item.first;
2858             if (!findSensorNameUsingSensorPath(sensor, *sensorsList,
2859                                                *sensorNames))
2860             {
2861                 BMCWEB_LOG_INFO << "Unable to find memberId " << item.first;
2862                 messages::resourceNotFound(sensorAsyncResp->res,
2863                                            item.second.second, item.first);
2864                 return;
2865             }
2866         }
2867         // Get the connection to which the memberId belongs
2868         auto getObjectsWithConnectionCb =
2869             [sensorAsyncResp, overrideMap](
2870                 const boost::container::flat_set<std::string>& /*connections*/,
2871                 const std::set<std::pair<std::string, std::string>>&
2872                     objectsWithConnection) {
2873                 if (objectsWithConnection.size() != overrideMap.size())
2874                 {
2875                     BMCWEB_LOG_INFO
2876                         << "Unable to find all objects with proper connection "
2877                         << objectsWithConnection.size() << " requested "
2878                         << overrideMap.size() << "\n";
2879                     messages::resourceNotFound(
2880                         sensorAsyncResp->res,
2881                         sensorAsyncResp->chassisSubNode ==
2882                                 sensors::node::thermal
2883                             ? "Temperatures"
2884                             : "Voltages",
2885                         "Count");
2886                     return;
2887                 }
2888                 for (const auto& item : objectsWithConnection)
2889                 {
2890                     sdbusplus::message::object_path path(item.first);
2891                     std::string sensorName = path.filename();
2892                     if (sensorName.empty())
2893                     {
2894                         messages::internalError(sensorAsyncResp->res);
2895                         return;
2896                     }
2897 
2898                     const auto& iterator = overrideMap.find(sensorName);
2899                     if (iterator == overrideMap.end())
2900                     {
2901                         BMCWEB_LOG_INFO << "Unable to find sensor object"
2902                                         << item.first << "\n";
2903                         messages::internalError(sensorAsyncResp->res);
2904                         return;
2905                     }
2906                     crow::connections::systemBus->async_method_call(
2907                         [sensorAsyncResp](const boost::system::error_code ec) {
2908                             if (ec)
2909                             {
2910                                 BMCWEB_LOG_DEBUG
2911                                     << "setOverrideValueStatus DBUS error: "
2912                                     << ec;
2913                                 messages::internalError(sensorAsyncResp->res);
2914                                 return;
2915                             }
2916                         },
2917                         item.second, item.first,
2918                         "org.freedesktop.DBus.Properties", "Set",
2919                         "xyz.openbmc_project.Sensor.Value", "Value",
2920                         std::variant<double>(iterator->second.first));
2921                 }
2922             };
2923         // Get object with connection for the given sensor name
2924         getObjectsWithConnection(sensorAsyncResp, sensorNames,
2925                                  std::move(getObjectsWithConnectionCb));
2926     };
2927     // get full sensor list for the given chassisId and cross verify the sensor.
2928     getChassis(sensorAsyncResp, std::move(getChassisSensorListCb));
2929 }
2930 
2931 inline bool isOverridingAllowed(const std::string& manufacturingModeStatus)
2932 {
2933     if (manufacturingModeStatus ==
2934         "xyz.openbmc_project.Control.Security.SpecialMode.Modes.Manufacturing")
2935     {
2936         return true;
2937     }
2938 
2939 #ifdef BMCWEB_ENABLE_VALIDATION_UNSECURE_FEATURE
2940     if (manufacturingModeStatus == "xyz.openbmc_project.Control.Security."
2941                                    "SpecialMode.Modes.ValidationUnsecure")
2942     {
2943         return true;
2944     }
2945 
2946 #endif
2947 
2948     return false;
2949 }
2950 
2951 /**
2952  * @brief Entry point for Checking the manufacturing mode before doing sensor
2953  * override values of given sensor
2954  *
2955  * @param res   response object
2956  * @param allCollections   Collections extract from sensors' request patch info
2957  * @param chassisSubNode   Chassis Node for which the query has to happen
2958  */
2959 inline void checkAndDoSensorsOverride(
2960     const std::shared_ptr<SensorsAsyncResp>& sensorAsyncResp,
2961     std::unordered_map<std::string, std::vector<nlohmann::json>>&
2962         allCollections)
2963 {
2964     BMCWEB_LOG_INFO << "checkAndDoSensorsOverride for subnode"
2965                     << sensorAsyncResp->chassisSubNode << "\n";
2966 
2967     const std::array<std::string, 1> interfaces = {
2968         "xyz.openbmc_project.Security.SpecialMode"};
2969 
2970     crow::connections::systemBus->async_method_call(
2971         [sensorAsyncResp, allCollections](const boost::system::error_code ec2,
2972                                           const GetSubTreeType& resp) mutable {
2973             if (ec2)
2974             {
2975                 BMCWEB_LOG_DEBUG
2976                     << "Error in querying GetSubTree with Object Mapper. "
2977                     << ec2;
2978                 messages::internalError(sensorAsyncResp->res);
2979                 return;
2980             }
2981 #ifdef BMCWEB_INSECURE_UNRESTRICTED_SENSOR_OVERRIDE
2982             // Proceed with sensor override
2983             setSensorsOverride(sensorAsyncResp, allCollections);
2984             return;
2985 #endif
2986 
2987             if (resp.size() != 1)
2988             {
2989                 BMCWEB_LOG_WARNING
2990                     << "Overriding sensor value is not allowed - Internal "
2991                        "error in querying SpecialMode property.";
2992                 messages::internalError(sensorAsyncResp->res);
2993                 return;
2994             }
2995             const std::string& path = resp[0].first;
2996             const std::string& serviceName = resp[0].second.begin()->first;
2997 
2998             if (path.empty() || serviceName.empty())
2999             {
3000                 BMCWEB_LOG_DEBUG
3001                     << "Path or service name is returned as empty. ";
3002                 messages::internalError(sensorAsyncResp->res);
3003                 return;
3004             }
3005 
3006             // Sensor override is allowed only in manufacturing mode or
3007             // validation unsecure mode .
3008             crow::connections::systemBus->async_method_call(
3009                 [sensorAsyncResp, allCollections,
3010                  path](const boost::system::error_code ec,
3011                        std::variant<std::string>& getManufactMode) mutable {
3012                     if (ec)
3013                     {
3014                         BMCWEB_LOG_DEBUG
3015                             << "Error in querying Special mode property " << ec;
3016                         messages::internalError(sensorAsyncResp->res);
3017                         return;
3018                     }
3019 
3020                     const std::string* manufacturingModeStatus =
3021                         std::get_if<std::string>(&getManufactMode);
3022 
3023                     if (nullptr == manufacturingModeStatus)
3024                     {
3025                         BMCWEB_LOG_DEBUG << "Sensor override mode is not "
3026                                             "Enabled. Returning ... ";
3027                         messages::internalError(sensorAsyncResp->res);
3028                         return;
3029                     }
3030 
3031                     if (isOverridingAllowed(*manufacturingModeStatus))
3032                     {
3033                         BMCWEB_LOG_INFO << "Manufacturing mode is Enabled. "
3034                                            "Proceeding further... ";
3035                         setSensorsOverride(sensorAsyncResp, allCollections);
3036                     }
3037                     else
3038                     {
3039                         BMCWEB_LOG_WARNING
3040                             << "Manufacturing mode is not Enabled...can't "
3041                                "Override the sensor value. ";
3042 
3043                         messages::actionNotSupported(
3044                             sensorAsyncResp->res,
3045                             "Overriding of Sensor Value for non "
3046                             "manufacturing mode");
3047                         return;
3048                     }
3049                 },
3050                 serviceName, path, "org.freedesktop.DBus.Properties", "Get",
3051                 "xyz.openbmc_project.Security.SpecialMode", "SpecialMode");
3052         },
3053 
3054         "xyz.openbmc_project.ObjectMapper",
3055         "/xyz/openbmc_project/object_mapper",
3056         "xyz.openbmc_project.ObjectMapper", "GetSubTree", "/", 5, interfaces);
3057 }
3058 
3059 /**
3060  * @brief Retrieves mapping of Redfish URIs to sensor value property to D-Bus
3061  * path of the sensor.
3062  *
3063  * Function builds valid Redfish response for sensor query of given chassis and
3064  * node. It then builds metadata about Redfish<->D-Bus correlations and provides
3065  * it to caller in a callback.
3066  *
3067  * @param chassis   Chassis for which retrieval should be performed
3068  * @param node  Node (group) of sensors. See sensors::node for supported values
3069  * @param mapComplete   Callback to be called with retrieval result
3070  */
3071 inline void retrieveUriToDbusMap(const std::string& chassis,
3072                                  const std::string& node,
3073                                  SensorsAsyncResp::DataCompleteCb&& mapComplete)
3074 {
3075     auto pathIt = sensors::dbus::paths.find(node);
3076     if (pathIt == sensors::dbus::paths.end())
3077     {
3078         BMCWEB_LOG_ERROR << "Wrong node provided : " << node;
3079         mapComplete(boost::beast::http::status::bad_request, {});
3080         return;
3081     }
3082 
3083     auto respBuffer = std::make_shared<crow::Response>();
3084     auto callback =
3085         [respBuffer, mapCompleteCb{std::move(mapComplete)}](
3086             const boost::beast::http::status status,
3087             const boost::container::flat_map<std::string, std::string>&
3088                 uriToDbus) { mapCompleteCb(status, uriToDbus); };
3089 
3090     auto resp = std::make_shared<SensorsAsyncResp>(
3091         *respBuffer, chassis, pathIt->second, node, std::move(callback));
3092     getChassisData(resp);
3093 }
3094 
3095 class SensorCollection : public Node
3096 {
3097   public:
3098     SensorCollection(App& app) :
3099         Node(app, "/redfish/v1/Chassis/<str>/Sensors/", std::string())
3100     {
3101         entityPrivileges = {
3102             {boost::beast::http::verb::get, {{"Login"}}},
3103             {boost::beast::http::verb::head, {{"Login"}}},
3104             {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
3105             {boost::beast::http::verb::put, {{"ConfigureManager"}}},
3106             {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
3107             {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
3108     }
3109 
3110   private:
3111     void doGet(crow::Response& res, const crow::Request&,
3112                const std::vector<std::string>& params) override
3113     {
3114         BMCWEB_LOG_DEBUG << "SensorCollection doGet enter";
3115         if (params.size() != 1)
3116         {
3117             BMCWEB_LOG_DEBUG << "SensorCollection doGet param size < 1";
3118             messages::internalError(res);
3119             res.end();
3120             return;
3121         }
3122 
3123         const std::string& chassisId = params[0];
3124         std::shared_ptr<SensorsAsyncResp> asyncResp =
3125             std::make_shared<SensorsAsyncResp>(
3126                 res, chassisId, sensors::dbus::paths.at(sensors::node::sensors),
3127                 sensors::node::sensors);
3128 
3129         auto getChassisCb =
3130             [asyncResp](
3131                 const std::shared_ptr<boost::container::flat_set<std::string>>&
3132                     sensorNames) {
3133                 BMCWEB_LOG_DEBUG << "getChassisCb enter";
3134 
3135                 nlohmann::json& entriesArray =
3136                     asyncResp->res.jsonValue["Members"];
3137                 for (auto& sensor : *sensorNames)
3138                 {
3139                     BMCWEB_LOG_DEBUG << "Adding sensor: " << sensor;
3140 
3141                     sdbusplus::message::object_path path(sensor);
3142                     std::string sensorName = path.filename();
3143                     if (sensorName.empty())
3144                     {
3145                         BMCWEB_LOG_ERROR << "Invalid sensor path: " << sensor;
3146                         messages::internalError(asyncResp->res);
3147                         return;
3148                     }
3149                     entriesArray.push_back(
3150                         {{"@odata.id",
3151                           "/redfish/v1/Chassis/" + asyncResp->chassisId + "/" +
3152                               asyncResp->chassisSubNode + "/" + sensorName}});
3153                 }
3154 
3155                 asyncResp->res.jsonValue["Members@odata.count"] =
3156                     entriesArray.size();
3157                 BMCWEB_LOG_DEBUG << "getChassisCb exit";
3158             };
3159 
3160         // Get set of sensors in chassis
3161         getChassis(asyncResp, std::move(getChassisCb));
3162         BMCWEB_LOG_DEBUG << "SensorCollection doGet exit";
3163     }
3164 };
3165 
3166 class Sensor : public Node
3167 {
3168   public:
3169     Sensor(App& app) :
3170         Node(app, "/redfish/v1/Chassis/<str>/Sensors/<str>/", std::string(),
3171              std::string())
3172     {
3173         entityPrivileges = {
3174             {boost::beast::http::verb::get, {{"Login"}}},
3175             {boost::beast::http::verb::head, {{"Login"}}},
3176             {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
3177             {boost::beast::http::verb::put, {{"ConfigureManager"}}},
3178             {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
3179             {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
3180     }
3181 
3182   private:
3183     void doGet(crow::Response& res, const crow::Request&,
3184                const std::vector<std::string>& params) override
3185     {
3186         BMCWEB_LOG_DEBUG << "Sensor doGet enter";
3187         if (params.size() != 2)
3188         {
3189             BMCWEB_LOG_DEBUG << "Sensor doGet param size < 2";
3190             messages::internalError(res);
3191             res.end();
3192             return;
3193         }
3194         const std::string& chassisId = params[0];
3195         std::shared_ptr<SensorsAsyncResp> asyncResp =
3196             std::make_shared<SensorsAsyncResp>(res, chassisId,
3197                                                std::vector<const char*>(),
3198                                                sensors::node::sensors);
3199 
3200         const std::string& sensorName = params[1];
3201         const std::array<const char*, 1> interfaces = {
3202             "xyz.openbmc_project.Sensor.Value"};
3203 
3204         // Get a list of all of the sensors that implement Sensor.Value
3205         // and get the path and service name associated with the sensor
3206         crow::connections::systemBus->async_method_call(
3207             [asyncResp, sensorName](const boost::system::error_code ec,
3208                                     const GetSubTreeType& subtree) {
3209                 BMCWEB_LOG_DEBUG << "respHandler1 enter";
3210                 if (ec)
3211                 {
3212                     messages::internalError(asyncResp->res);
3213                     BMCWEB_LOG_ERROR << "Sensor getSensorPaths resp_handler: "
3214                                      << "Dbus error " << ec;
3215                     return;
3216                 }
3217 
3218                 GetSubTreeType::const_iterator it = std::find_if(
3219                     subtree.begin(), subtree.end(),
3220                     [sensorName](
3221                         const std::pair<
3222                             std::string,
3223                             std::vector<std::pair<std::string,
3224                                                   std::vector<std::string>>>>&
3225                             object) {
3226                         sdbusplus::message::object_path path(object.first);
3227                         std::string name = path.filename();
3228                         if (name.empty())
3229                         {
3230                             BMCWEB_LOG_ERROR << "Invalid sensor path: "
3231                                              << object.first;
3232                             return false;
3233                         }
3234 
3235                         return name == sensorName;
3236                     });
3237 
3238                 if (it == subtree.end())
3239                 {
3240                     BMCWEB_LOG_ERROR << "Could not find path for sensor: "
3241                                      << sensorName;
3242                     messages::resourceNotFound(asyncResp->res, "Sensor",
3243                                                sensorName);
3244                     return;
3245                 }
3246                 std::string_view sensorPath = (*it).first;
3247                 BMCWEB_LOG_DEBUG << "Found sensor path for sensor '"
3248                                  << sensorName << "': " << sensorPath;
3249 
3250                 const std::shared_ptr<boost::container::flat_set<std::string>>
3251                     sensorList = std::make_shared<
3252                         boost::container::flat_set<std::string>>();
3253 
3254                 sensorList->emplace(sensorPath);
3255                 processSensorList(asyncResp, sensorList);
3256                 BMCWEB_LOG_DEBUG << "respHandler1 exit";
3257             },
3258             "xyz.openbmc_project.ObjectMapper",
3259             "/xyz/openbmc_project/object_mapper",
3260             "xyz.openbmc_project.ObjectMapper", "GetSubTree",
3261             "/xyz/openbmc_project/sensors", 2, interfaces);
3262     }
3263 };
3264 
3265 } // namespace redfish
3266