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