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