xref: /openbmc/bmcweb/features/redfish/lib/sensors.hpp (revision 49c53ac9b88fbf71f55076121a1605a760fa0096)
1 /*
2 // Copyright (c) 2018 Intel Corporation
3 //
4 // Licensed under the Apache License, Version 2.0 (the "License");
5 // you may not use this file except in compliance with the License.
6 // You may obtain a copy of the License at
7 //
8 //      http://www.apache.org/licenses/LICENSE-2.0
9 //
10 // Unless required by applicable law or agreed to in writing, software
11 // distributed under the License is distributed on an "AS IS" BASIS,
12 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 // See the License for the specific language governing permissions and
14 // limitations under the License.
15 */
16 #pragma once
17 
18 #include <math.h>
19 
20 #include <boost/algorithm/string/predicate.hpp>
21 #include <boost/algorithm/string/split.hpp>
22 #include <boost/container/flat_map.hpp>
23 #include <boost/range/algorithm/replace_copy_if.hpp>
24 #include <dbus_singleton.hpp>
25 #include <utils/json_utils.hpp>
26 #include <variant>
27 
28 namespace redfish
29 {
30 
31 constexpr const char* dbusSensorPrefix = "/xyz/openbmc_project/sensors/";
32 
33 using GetSubTreeType = std::vector<
34     std::pair<std::string,
35               std::vector<std::pair<std::string, std::vector<std::string>>>>>;
36 
37 using SensorVariant = std::variant<int64_t, double>;
38 
39 using ManagedObjectsVectorType = std::vector<std::pair<
40     sdbusplus::message::object_path,
41     boost::container::flat_map<
42         std::string, boost::container::flat_map<std::string, SensorVariant>>>>;
43 
44 /**
45  * SensorsAsyncResp
46  * Gathers data needed for response processing after async calls are done
47  */
48 class SensorsAsyncResp
49 {
50   public:
51     SensorsAsyncResp(crow::Response& response, const std::string& chassisId,
52                      const std::initializer_list<const char*> types,
53                      const std::string& subNode) :
54         res(response),
55         chassisId(chassisId), types(types), chassisSubNode(subNode)
56     {
57     }
58 
59     ~SensorsAsyncResp()
60     {
61         if (res.result() == boost::beast::http::status::internal_server_error)
62         {
63             // Reset the json object to clear out any data that made it in
64             // before the error happened todo(ed) handle error condition with
65             // proper code
66             res.jsonValue = nlohmann::json::object();
67         }
68         res.end();
69     }
70 
71     crow::Response& res;
72     std::string chassisId{};
73     const std::vector<const char*> types;
74     std::string chassisSubNode{};
75 };
76 
77 /**
78  * @brief Get objects with connection necessary for sensors
79  * @param SensorsAsyncResp Pointer to object holding response data
80  * @param sensorNames Sensors retrieved from chassis
81  * @param callback Callback for processing gathered connections
82  */
83 template <typename Callback>
84 void getObjectsWithConnection(
85     std::shared_ptr<SensorsAsyncResp> SensorsAsyncResp,
86     const std::shared_ptr<boost::container::flat_set<std::string>> sensorNames,
87     Callback&& callback)
88 {
89     BMCWEB_LOG_DEBUG << "getObjectsWithConnection enter";
90     const std::string path = "/xyz/openbmc_project/sensors";
91     const std::array<std::string, 1> interfaces = {
92         "xyz.openbmc_project.Sensor.Value"};
93 
94     // Response handler for parsing objects subtree
95     auto respHandler = [callback{std::move(callback)}, SensorsAsyncResp,
96                         sensorNames](const boost::system::error_code ec,
97                                      const GetSubTreeType& subtree) {
98         BMCWEB_LOG_DEBUG << "getObjectsWithConnection resp_handler enter";
99         if (ec)
100         {
101             messages::internalError(SensorsAsyncResp->res);
102             BMCWEB_LOG_ERROR
103                 << "getObjectsWithConnection resp_handler: Dbus error " << ec;
104             return;
105         }
106 
107         BMCWEB_LOG_DEBUG << "Found " << subtree.size() << " subtrees";
108 
109         // Make unique list of connections only for requested sensor types and
110         // found in the chassis
111         boost::container::flat_set<std::string> connections;
112         std::set<std::pair<std::string, std::string>> objectsWithConnection;
113         // Intrinsic to avoid malloc.  Most systems will have < 8 sensor
114         // producers
115         connections.reserve(8);
116 
117         BMCWEB_LOG_DEBUG << "sensorNames list count: " << sensorNames->size();
118         for (const std::string& tsensor : *sensorNames)
119         {
120             BMCWEB_LOG_DEBUG << "Sensor to find: " << tsensor;
121         }
122 
123         for (const std::pair<
124                  std::string,
125                  std::vector<std::pair<std::string, std::vector<std::string>>>>&
126                  object : subtree)
127         {
128             if (sensorNames->find(object.first) != sensorNames->end())
129             {
130                 for (const std::pair<std::string, std::vector<std::string>>&
131                          objData : object.second)
132                 {
133                     BMCWEB_LOG_DEBUG << "Adding connection: " << objData.first;
134                     connections.insert(objData.first);
135                     objectsWithConnection.insert(
136                         std::make_pair(object.first, objData.first));
137                 }
138             }
139         }
140         BMCWEB_LOG_DEBUG << "Found " << connections.size() << " connections";
141         callback(std::move(connections), std::move(objectsWithConnection));
142         BMCWEB_LOG_DEBUG << "getObjectsWithConnection resp_handler exit";
143     };
144     // Make call to ObjectMapper to find all sensors objects
145     crow::connections::systemBus->async_method_call(
146         std::move(respHandler), "xyz.openbmc_project.ObjectMapper",
147         "/xyz/openbmc_project/object_mapper",
148         "xyz.openbmc_project.ObjectMapper", "GetSubTree", path, 2, interfaces);
149     BMCWEB_LOG_DEBUG << "getObjectsWithConnection exit";
150 }
151 
152 /**
153  * @brief Create connections necessary for sensors
154  * @param SensorsAsyncResp Pointer to object holding response data
155  * @param sensorNames Sensors retrieved from chassis
156  * @param callback Callback for processing gathered connections
157  */
158 template <typename Callback>
159 void getConnections(
160     std::shared_ptr<SensorsAsyncResp> SensorsAsyncResp,
161     const std::shared_ptr<boost::container::flat_set<std::string>> sensorNames,
162     Callback&& callback)
163 {
164     auto objectsWithConnectionCb =
165         [callback](const boost::container::flat_set<std::string>& connections,
166                    const std::set<std::pair<std::string, std::string>>&
167                        objectsWithConnection) {
168             callback(std::move(connections));
169         };
170     getObjectsWithConnection(SensorsAsyncResp, sensorNames,
171                              std::move(objectsWithConnectionCb));
172 }
173 
174 /**
175  * @brief Gets all DBus sensor names.
176  *
177  * Finds the sensor names asynchronously.  Invokes callback when information has
178  * been obtained.
179  *
180  * The callback must have the following signature:
181  *   @code
182  *   callback(boost::container::flat_set<std::string>& sensorNames)
183  *   @endcode
184  *
185  * @param SensorsAsyncResp Pointer to object holding response data.
186  * @param callback Callback to invoke when sensor names obtained.
187  */
188 template <typename Callback>
189 void getAllSensors(std::shared_ptr<SensorsAsyncResp> SensorsAsyncResp,
190                    Callback&& callback)
191 {
192     BMCWEB_LOG_DEBUG << "getAllSensors enter";
193     const std::array<std::string, 1> interfaces = {
194         "xyz.openbmc_project.Sensor.Value"};
195 
196     // Response handler for GetSubTreePaths DBus method
197     auto respHandler = [callback{std::move(callback)}, SensorsAsyncResp](
198                            const boost::system::error_code ec,
199                            const std::vector<std::string>& sensorList) {
200         BMCWEB_LOG_DEBUG << "getAllSensors respHandler enter";
201         if (ec)
202         {
203             messages::internalError(SensorsAsyncResp->res);
204             BMCWEB_LOG_ERROR << "getAllSensors respHandler: DBus error " << ec;
205             return;
206         }
207         boost::container::flat_set<std::string> sensorNames;
208         for (const std::string& sensor : sensorList)
209         {
210             std::size_t lastPos = sensor.rfind("/");
211             if (lastPos == std::string::npos)
212             {
213                 BMCWEB_LOG_ERROR << "Failed to find '/' in " << sensor;
214                 continue;
215             }
216             std::string sensorName = sensor.substr(lastPos + 1);
217             sensorNames.emplace(sensorName);
218             BMCWEB_LOG_DEBUG << "Added sensor name " << sensorName;
219         }
220         callback(sensorNames);
221         BMCWEB_LOG_DEBUG << "getAllSensors respHandler exit";
222     };
223 
224     // Query mapper for all DBus sensor object paths
225     crow::connections::systemBus->async_method_call(
226         std::move(respHandler), "xyz.openbmc_project.ObjectMapper",
227         "/xyz/openbmc_project/object_mapper",
228         "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths",
229         "/xyz/openbmc_project/sensors", int32_t(0), interfaces);
230     BMCWEB_LOG_DEBUG << "getAllSensors exit";
231 }
232 
233 /**
234  * @brief Shrinks the list of sensors for processing
235  * @param SensorsAysncResp  The class holding the Redfish response
236  * @param allSensors  A list of all the sensors associated to the
237  * chassis element (i.e. baseboard, front panel, etc...)
238  * @param activeSensors A list that is a reduction of the incoming
239  * allSensors list.  Eliminate Thermal sensors when a Power request is
240  * made, and eliminate Power sensors when a Thermal request is made.
241  */
242 void reduceSensorList(
243     std::shared_ptr<SensorsAsyncResp> SensorsAsyncResp,
244     const std::vector<std::string>* allSensors,
245     std::shared_ptr<boost::container::flat_set<std::string>> activeSensors)
246 {
247     if (SensorsAsyncResp == nullptr)
248     {
249         return;
250     }
251     if ((allSensors == nullptr) || (activeSensors == nullptr))
252     {
253         messages::resourceNotFound(
254             SensorsAsyncResp->res, SensorsAsyncResp->chassisSubNode,
255             SensorsAsyncResp->chassisSubNode == "Thermal" ? "Temperatures"
256                                                           : "Voltages");
257 
258         return;
259     }
260     if (allSensors->empty())
261     {
262         // Nothing to do, the activeSensors object is also empty
263         return;
264     }
265 
266     for (const char* type : SensorsAsyncResp->types)
267     {
268         for (const std::string& sensor : *allSensors)
269         {
270             if (boost::starts_with(sensor, type))
271             {
272                 activeSensors->emplace(sensor);
273             }
274         }
275     }
276 }
277 
278 /**
279  * @brief Retrieves requested chassis sensors and redundancy data from DBus .
280  * @param SensorsAsyncResp   Pointer to object holding response data
281  * @param callback  Callback for next step in gathered sensor processing
282  */
283 template <typename Callback>
284 void getChassis(std::shared_ptr<SensorsAsyncResp> sensorsAsyncResp,
285                 Callback&& callback)
286 {
287     BMCWEB_LOG_DEBUG << "getChassis enter";
288     const std::array<const char*, 3> interfaces = {
289         "xyz.openbmc_project.Inventory.Item.Board",
290         "xyz.openbmc_project.Inventory.Item.Chassis",
291         "xyz.openbmc_project.Inventory.Item.PowerSupply"};
292     auto respHandler = [callback{std::move(callback)}, sensorsAsyncResp](
293                            const boost::system::error_code ec,
294                            const std::vector<std::string>& chassisPaths) {
295         BMCWEB_LOG_DEBUG << "getChassis respHandler enter";
296         if (ec)
297         {
298             BMCWEB_LOG_ERROR << "getChassis respHandler DBUS error: " << ec;
299             messages::internalError(sensorsAsyncResp->res);
300             return;
301         }
302 
303         const std::string* chassisPath = nullptr;
304         std::string chassisName;
305         for (const std::string& chassis : chassisPaths)
306         {
307             std::size_t lastPos = chassis.rfind("/");
308             if (lastPos == std::string::npos)
309             {
310                 BMCWEB_LOG_ERROR << "Failed to find '/' in " << chassis;
311                 continue;
312             }
313             chassisName = chassis.substr(lastPos + 1);
314             if (chassisName == sensorsAsyncResp->chassisId)
315             {
316                 chassisPath = &chassis;
317                 break;
318             }
319         }
320         if (chassisPath == nullptr)
321         {
322             messages::resourceNotFound(sensorsAsyncResp->res, "Chassis",
323                                        sensorsAsyncResp->chassisId);
324             return;
325         }
326 
327         const std::string& chassisSubNode = sensorsAsyncResp->chassisSubNode;
328         if (chassisSubNode == "Power")
329         {
330             sensorsAsyncResp->res.jsonValue["@odata.type"] =
331                 "#Power.v1_5_2.Power";
332         }
333         else if (chassisSubNode == "Thermal")
334         {
335             sensorsAsyncResp->res.jsonValue["@odata.type"] =
336                 "#Thermal.v1_4_0.Thermal";
337         }
338         sensorsAsyncResp->res.jsonValue["@odata.id"] =
339             "/redfish/v1/Chassis/" + sensorsAsyncResp->chassisId + "/" +
340             chassisSubNode;
341 
342         sensorsAsyncResp->res.jsonValue["@odata.context"] =
343             "/redfish/v1/$metadata#" + chassisSubNode + "." + chassisSubNode;
344         sensorsAsyncResp->res.jsonValue["Id"] = chassisSubNode;
345         sensorsAsyncResp->res.jsonValue["Name"] = chassisSubNode;
346 
347         // Get the list of sensors for this Chassis element
348         std::string sensorPath = *chassisPath + "/sensors";
349         crow::connections::systemBus->async_method_call(
350             [sensorsAsyncResp, callback{std::move(callback)}](
351                 const boost::system::error_code ec,
352                 const std::variant<std::vector<std::string>>&
353                     variantEndpoints) {
354                 if (ec)
355                 {
356                     if (ec.value() != EBADR)
357                     {
358                         messages::internalError(sensorsAsyncResp->res);
359                         return;
360                     }
361                 }
362                 const std::vector<std::string>* nodeSensorList =
363                     std::get_if<std::vector<std::string>>(&(variantEndpoints));
364                 if (nodeSensorList == nullptr)
365                 {
366                     messages::resourceNotFound(
367                         sensorsAsyncResp->res, sensorsAsyncResp->chassisSubNode,
368                         sensorsAsyncResp->chassisSubNode == "Thermal"
369                             ? "Temperatures"
370                             : "Voltages");
371                     return;
372                 }
373                 const std::shared_ptr<boost::container::flat_set<std::string>>
374                     culledSensorList = std::make_shared<
375                         boost::container::flat_set<std::string>>();
376                 reduceSensorList(sensorsAsyncResp, nodeSensorList,
377                                  culledSensorList);
378                 callback(culledSensorList);
379             },
380             "xyz.openbmc_project.ObjectMapper", sensorPath,
381             "org.freedesktop.DBus.Properties", "Get",
382             "xyz.openbmc_project.Association", "endpoints");
383     };
384 
385     // Get the Chassis Collection
386     crow::connections::systemBus->async_method_call(
387         respHandler, "xyz.openbmc_project.ObjectMapper",
388         "/xyz/openbmc_project/object_mapper",
389         "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths",
390         "/xyz/openbmc_project/inventory", int32_t(0), interfaces);
391     BMCWEB_LOG_DEBUG << "getChassis exit";
392 }
393 
394 /**
395  * @brief Finds all DBus object paths that implement ObjectManager.
396  *
397  * Creates a mapping from the associated connection name to the object path.
398  *
399  * Finds the object paths asynchronously.  Invokes callback when information has
400  * been obtained.
401  *
402  * The callback must have the following signature:
403  *   @code
404  *   callback(const boost::container::flat_map<std::string,
405  *            std::string>& objectMgrPaths)
406  *   @endcode
407  *
408  * @param sensorsAsyncResp Pointer to object holding response data.
409  * @param callback Callback to invoke when object paths obtained.
410  */
411 template <typename Callback>
412 void getObjectManagerPaths(std::shared_ptr<SensorsAsyncResp> SensorsAsyncResp,
413                            Callback&& callback)
414 {
415     BMCWEB_LOG_DEBUG << "getObjectManagerPaths enter";
416     const std::array<std::string, 1> interfaces = {
417         "org.freedesktop.DBus.ObjectManager"};
418 
419     // Response handler for GetSubTree DBus method
420     auto respHandler = [callback{std::move(callback)},
421                         SensorsAsyncResp](const boost::system::error_code ec,
422                                           const GetSubTreeType& subtree) {
423         BMCWEB_LOG_DEBUG << "getObjectManagerPaths respHandler enter";
424         if (ec)
425         {
426             messages::internalError(SensorsAsyncResp->res);
427             BMCWEB_LOG_ERROR << "getObjectManagerPaths respHandler: DBus error "
428                              << ec;
429             return;
430         }
431 
432         // Loop over returned object paths
433         boost::container::flat_map<std::string, std::string> objectMgrPaths;
434         for (const std::pair<
435                  std::string,
436                  std::vector<std::pair<std::string, std::vector<std::string>>>>&
437                  object : subtree)
438         {
439             // Loop over connections for current object path
440             const std::string& objectPath = object.first;
441             for (const std::pair<std::string, std::vector<std::string>>&
442                      objData : object.second)
443             {
444                 // Add mapping from connection to object path
445                 const std::string& connection = objData.first;
446                 objectMgrPaths[connection] = objectPath;
447                 BMCWEB_LOG_DEBUG << "Added mapping " << connection << " -> "
448                                  << objectPath;
449             }
450         }
451         callback(std::move(objectMgrPaths));
452         BMCWEB_LOG_DEBUG << "getObjectManagerPaths respHandler exit";
453     };
454 
455     // Query mapper for all DBus object paths that implement ObjectManager
456     crow::connections::systemBus->async_method_call(
457         std::move(respHandler), "xyz.openbmc_project.ObjectMapper",
458         "/xyz/openbmc_project/object_mapper",
459         "xyz.openbmc_project.ObjectMapper", "GetSubTree", "/", int32_t(0),
460         interfaces);
461     BMCWEB_LOG_DEBUG << "getObjectManagerPaths exit";
462 }
463 
464 /**
465  * @brief Builds a json sensor representation of a sensor.
466  * @param sensorName  The name of the sensor to be built
467  * @param sensorType  The type (temperature, fan_tach, etc) of the sensor to
468  * build
469  * @param interfacesDict  A dictionary of the interfaces and properties of said
470  * interfaces to be built from
471  * @param sensor_json  The json object to fill
472  */
473 void objectInterfacesToJson(
474     const std::string& sensorName, const std::string& sensorType,
475     const boost::container::flat_map<
476         std::string, boost::container::flat_map<std::string, SensorVariant>>&
477         interfacesDict,
478     nlohmann::json& sensor_json)
479 {
480     // We need a value interface before we can do anything with it
481     auto valueIt = interfacesDict.find("xyz.openbmc_project.Sensor.Value");
482     if (valueIt == interfacesDict.end())
483     {
484         BMCWEB_LOG_ERROR << "Sensor doesn't have a value interface";
485         return;
486     }
487 
488     // Assume values exist as is (10^0 == 1) if no scale exists
489     int64_t scaleMultiplier = 0;
490 
491     auto scaleIt = valueIt->second.find("Scale");
492     // If a scale exists, pull value as int64, and use the scaling.
493     if (scaleIt != valueIt->second.end())
494     {
495         const int64_t* int64Value = std::get_if<int64_t>(&scaleIt->second);
496         if (int64Value != nullptr)
497         {
498             scaleMultiplier = *int64Value;
499         }
500     }
501 
502     sensor_json["MemberId"] = sensorName;
503     sensor_json["Name"] = sensorName;
504     sensor_json["Status"]["State"] = "Enabled";
505     sensor_json["Status"]["Health"] = "OK";
506 
507     // Parameter to set to override the type we get from dbus, and force it to
508     // int, regardless of what is available.  This is used for schemas like fan,
509     // that require integers, not floats.
510     bool forceToInt = false;
511 
512     const char* unit = "Reading";
513     if (sensorType == "temperature")
514     {
515         unit = "ReadingCelsius";
516         sensor_json["@odata.type"] = "#Thermal.v1_3_0.Temperature";
517         // TODO(ed) Documentation says that path should be type fan_tach,
518         // implementation seems to implement fan
519     }
520     else if (sensorType == "fan" || sensorType == "fan_tach")
521     {
522         unit = "Reading";
523         sensor_json["ReadingUnits"] = "RPM";
524         sensor_json["@odata.type"] = "#Thermal.v1_3_0.Fan";
525         forceToInt = true;
526     }
527     else if (sensorType == "fan_pwm")
528     {
529         unit = "Reading";
530         sensor_json["ReadingUnits"] = "Percent";
531         sensor_json["@odata.type"] = "#Thermal.v1_3_0.Fan";
532         forceToInt = true;
533     }
534     else if (sensorType == "voltage")
535     {
536         unit = "ReadingVolts";
537         sensor_json["@odata.type"] = "#Power.v1_0_0.Voltage";
538     }
539     else if (sensorType == "power")
540     {
541         std::string sensorNameLower =
542             boost::algorithm::to_lower_copy(sensorName);
543 
544         if (sensorNameLower.find("input") != std::string::npos)
545         {
546             unit = "PowerInputWatts";
547         }
548         else
549         {
550             unit = "PowerOutputWatts";
551         }
552     }
553     else
554     {
555         BMCWEB_LOG_ERROR << "Redfish cannot map object type for " << sensorName;
556         return;
557     }
558     // Map of dbus interface name, dbus property name and redfish property_name
559     std::vector<std::tuple<const char*, const char*, const char*>> properties;
560     properties.reserve(7);
561 
562     properties.emplace_back("xyz.openbmc_project.Sensor.Value", "Value", unit);
563 
564     // If sensor type doesn't map to Redfish PowerSupply, add threshold props
565     if ((sensorType != "current") && (sensorType != "power"))
566     {
567         properties.emplace_back("xyz.openbmc_project.Sensor.Threshold.Warning",
568                                 "WarningHigh", "UpperThresholdNonCritical");
569         properties.emplace_back("xyz.openbmc_project.Sensor.Threshold.Warning",
570                                 "WarningLow", "LowerThresholdNonCritical");
571         properties.emplace_back("xyz.openbmc_project.Sensor.Threshold.Critical",
572                                 "CriticalHigh", "UpperThresholdCritical");
573         properties.emplace_back("xyz.openbmc_project.Sensor.Threshold.Critical",
574                                 "CriticalLow", "LowerThresholdCritical");
575     }
576 
577     // TODO Need to get UpperThresholdFatal and LowerThresholdFatal
578 
579     if (sensorType == "temperature")
580     {
581         properties.emplace_back("xyz.openbmc_project.Sensor.Value", "MinValue",
582                                 "MinReadingRangeTemp");
583         properties.emplace_back("xyz.openbmc_project.Sensor.Value", "MaxValue",
584                                 "MaxReadingRangeTemp");
585     }
586     else if ((sensorType != "current") && (sensorType != "power"))
587     {
588         // Sensor type doesn't map to Redfish PowerSupply; add min/max props
589         properties.emplace_back("xyz.openbmc_project.Sensor.Value", "MinValue",
590                                 "MinReadingRange");
591         properties.emplace_back("xyz.openbmc_project.Sensor.Value", "MaxValue",
592                                 "MaxReadingRange");
593     }
594 
595     for (const std::tuple<const char*, const char*, const char*>& p :
596          properties)
597     {
598         auto interfaceProperties = interfacesDict.find(std::get<0>(p));
599         if (interfaceProperties != interfacesDict.end())
600         {
601             auto valueIt = interfaceProperties->second.find(std::get<1>(p));
602             if (valueIt != interfaceProperties->second.end())
603             {
604                 const SensorVariant& valueVariant = valueIt->second;
605                 nlohmann::json& valueIt = sensor_json[std::get<2>(p)];
606                 // Attempt to pull the int64 directly
607                 const int64_t* int64Value = std::get_if<int64_t>(&valueVariant);
608 
609                 const double* doubleValue = std::get_if<double>(&valueVariant);
610                 double temp = 0.0;
611                 if (int64Value != nullptr)
612                 {
613                     temp = *int64Value;
614                 }
615                 else if (doubleValue != nullptr)
616                 {
617                     temp = *doubleValue;
618                 }
619                 else
620                 {
621                     BMCWEB_LOG_ERROR
622                         << "Got value interface that wasn't int or double";
623                     continue;
624                 }
625                 temp = temp * std::pow(10, scaleMultiplier);
626                 if (forceToInt)
627                 {
628                     valueIt = static_cast<int64_t>(temp);
629                 }
630                 else
631                 {
632                     valueIt = temp;
633                 }
634             }
635         }
636     }
637     BMCWEB_LOG_DEBUG << "Added sensor " << sensorName;
638 }
639 
640 static void
641     populateFanRedundancy(std::shared_ptr<SensorsAsyncResp> sensorsAsyncResp)
642 {
643     crow::connections::systemBus->async_method_call(
644         [sensorsAsyncResp](const boost::system::error_code ec,
645                            const GetSubTreeType& resp) {
646             if (ec)
647             {
648                 return; // don't have to have this interface
649             }
650             for (const std::pair<std::string,
651                                  std::vector<std::pair<
652                                      std::string, std::vector<std::string>>>>&
653                      pathPair : resp)
654             {
655                 const std::string& path = pathPair.first;
656                 const std::vector<
657                     std::pair<std::string, std::vector<std::string>>>& objDict =
658                     pathPair.second;
659                 if (objDict.empty())
660                 {
661                     continue; // this should be impossible
662                 }
663 
664                 const std::string& owner = objDict.begin()->first;
665                 crow::connections::systemBus->async_method_call(
666                     [path, owner,
667                      sensorsAsyncResp](const boost::system::error_code ec,
668                                        std::variant<std::vector<std::string>>
669                                            variantEndpoints) {
670                         if (ec)
671                         {
672                             return; // if they don't have an association we
673                                     // can't tell what chassis is
674                         }
675                         // verify part of the right chassis
676                         auto endpoints = std::get_if<std::vector<std::string>>(
677                             &variantEndpoints);
678 
679                         if (endpoints == nullptr)
680                         {
681                             BMCWEB_LOG_ERROR << "Invalid association interface";
682                             messages::internalError(sensorsAsyncResp->res);
683                             return;
684                         }
685 
686                         auto found = std::find_if(
687                             endpoints->begin(), endpoints->end(),
688                             [sensorsAsyncResp](const std::string& entry) {
689                                 return entry.find(
690                                            sensorsAsyncResp->chassisId) !=
691                                        std::string::npos;
692                             });
693 
694                         if (found == endpoints->end())
695                         {
696                             return;
697                         }
698                         crow::connections::systemBus->async_method_call(
699                             [path, sensorsAsyncResp](
700                                 const boost::system::error_code ec,
701                                 const boost::container::flat_map<
702                                     std::string,
703                                     std::variant<uint8_t,
704                                                  std::vector<std::string>,
705                                                  std::string>>& ret) {
706                                 if (ec)
707                                 {
708                                     return; // don't have to have this
709                                             // interface
710                                 }
711                                 auto findFailures = ret.find("AllowedFailures");
712                                 auto findCollection = ret.find("Collection");
713                                 auto findStatus = ret.find("Status");
714 
715                                 if (findFailures == ret.end() ||
716                                     findCollection == ret.end() ||
717                                     findStatus == ret.end())
718                                 {
719                                     BMCWEB_LOG_ERROR
720                                         << "Invalid redundancy interface";
721                                     messages::internalError(
722                                         sensorsAsyncResp->res);
723                                     return;
724                                 }
725 
726                                 auto allowedFailures = std::get_if<uint8_t>(
727                                     &(findFailures->second));
728                                 auto collection =
729                                     std::get_if<std::vector<std::string>>(
730                                         &(findCollection->second));
731                                 auto status = std::get_if<std::string>(
732                                     &(findStatus->second));
733 
734                                 if (allowedFailures == nullptr ||
735                                     collection == nullptr || status == nullptr)
736                                 {
737 
738                                     BMCWEB_LOG_ERROR
739                                         << "Invalid redundancy interface "
740                                            "types";
741                                     messages::internalError(
742                                         sensorsAsyncResp->res);
743                                     return;
744                                 }
745                                 size_t lastSlash = path.rfind("/");
746                                 if (lastSlash == std::string::npos)
747                                 {
748                                     // this should be impossible
749                                     messages::internalError(
750                                         sensorsAsyncResp->res);
751                                     return;
752                                 }
753                                 std::string name = path.substr(lastSlash + 1);
754                                 std::replace(name.begin(), name.end(), '_',
755                                              ' ');
756 
757                                 std::string health;
758 
759                                 if (boost::ends_with(*status, "Full"))
760                                 {
761                                     health = "OK";
762                                 }
763                                 else if (boost::ends_with(*status, "Degraded"))
764                                 {
765                                     health = "Warning";
766                                 }
767                                 else
768                                 {
769                                     health = "Critical";
770                                 }
771                                 std::vector<nlohmann::json> redfishCollection;
772                                 const auto& fanRedfish =
773                                     sensorsAsyncResp->res.jsonValue["Fans"];
774                                 for (const std::string& item : *collection)
775                                 {
776                                     lastSlash = item.rfind("/");
777                                     // make a copy as collection is const
778                                     std::string itemName =
779                                         item.substr(lastSlash + 1);
780                                     /*
781                                     todo(ed): merge patch that fixes the names
782                                     std::replace(itemName.begin(),
783                                                  itemName.end(), '_', ' ');*/
784                                     auto schemaItem = std::find_if(
785                                         fanRedfish.begin(), fanRedfish.end(),
786                                         [itemName](const nlohmann::json& fan) {
787                                             return fan["MemberId"] == itemName;
788                                         });
789                                     if (schemaItem != fanRedfish.end())
790                                     {
791                                         redfishCollection.push_back(
792                                             {{"@odata.id",
793                                               (*schemaItem)["@odata.id"]}});
794                                     }
795                                     else
796                                     {
797                                         BMCWEB_LOG_ERROR
798                                             << "failed to find fan in schema";
799                                         messages::internalError(
800                                             sensorsAsyncResp->res);
801                                         return;
802                                     }
803                                 }
804 
805                                 auto& resp = sensorsAsyncResp->res
806                                                  .jsonValue["Redundancy"];
807                                 resp.push_back(
808                                     {{"@odata.id",
809                                       "/refish/v1/Chassis/" +
810                                           sensorsAsyncResp->chassisId + "/" +
811                                           sensorsAsyncResp->chassisSubNode +
812                                           "#/Redundancy/" +
813                                           std::to_string(resp.size())},
814                                      {"@odata.type",
815                                       "#Redundancy.v1_3_2.Redundancy"},
816                                      {"MinNumNeeded",
817                                       collection->size() - *allowedFailures},
818                                      {"MemberId", name},
819                                      {"Mode", "N+m"},
820                                      {"Name", name},
821                                      {"RedundancySet", redfishCollection},
822                                      {"Status",
823                                       {{"Health", health},
824                                        {"State", "Enabled"}}}});
825                             },
826                             owner, path, "org.freedesktop.DBus.Properties",
827                             "GetAll",
828                             "xyz.openbmc_project.Control.FanRedundancy");
829                     },
830                     "xyz.openbmc_project.ObjectMapper", path + "/inventory",
831                     "org.freedesktop.DBus.Properties", "Get",
832                     "xyz.openbmc_project.Association", "endpoints");
833             }
834         },
835         "xyz.openbmc_project.ObjectMapper",
836         "/xyz/openbmc_project/object_mapper",
837         "xyz.openbmc_project.ObjectMapper", "GetSubTree",
838         "/xyz/openbmc_project/control", 2,
839         std::array<const char*, 1>{
840             "xyz.openbmc_project.Control.FanRedundancy"});
841 }
842 
843 void sortJSONResponse(std::shared_ptr<SensorsAsyncResp> SensorsAsyncResp)
844 {
845     nlohmann::json& response = SensorsAsyncResp->res.jsonValue;
846     std::array<std::string, 2> sensorHeaders{"Temperatures", "Fans"};
847     if (SensorsAsyncResp->chassisSubNode == "Power")
848     {
849         sensorHeaders = {"Voltages", "PowerSupplies"};
850     }
851     for (const std::string& sensorGroup : sensorHeaders)
852     {
853         nlohmann::json::iterator entry = response.find(sensorGroup);
854         if (entry != response.end())
855         {
856             std::sort(entry->begin(), entry->end(),
857                       [](nlohmann::json& c1, nlohmann::json& c2) {
858                           return c1["Name"] < c2["Name"];
859                       });
860 
861             // add the index counts to the end of each entry
862             size_t count = 0;
863             for (nlohmann::json& sensorJson : *entry)
864             {
865                 nlohmann::json::iterator odata = sensorJson.find("@odata.id");
866                 if (odata == sensorJson.end())
867                 {
868                     continue;
869                 }
870                 std::string* value = odata->get_ptr<std::string*>();
871                 if (value != nullptr)
872                 {
873                     *value += std::to_string(count);
874                     count++;
875                 }
876             }
877         }
878     }
879 }
880 
881 /**
882  * @brief Gets the values of the specified sensors.
883  *
884  * Stores the results as JSON in the SensorsAsyncResp.
885  *
886  * Gets the sensor values asynchronously.  Stores the results later when the
887  * information has been obtained.
888  *
889  * The sensorNames set contains all sensors for the current chassis.
890  * SensorsAsyncResp contains the requested sensor types.  Only sensors of a
891  * requested type are included in the JSON output.
892  *
893  * To minimize the number of DBus calls, the DBus method
894  * org.freedesktop.DBus.ObjectManager.GetManagedObjects() is used to get the
895  * values of all sensors provided by a connection (service).
896  *
897  * The connections set contains all the connections that provide sensor values.
898  *
899  * The objectMgrPaths map contains mappings from a connection name to the
900  * corresponding DBus object path that implements ObjectManager.
901  *
902  * @param SensorsAsyncResp Pointer to object holding response data.
903  * @param sensorNames All sensors within the current chassis.
904  * @param connections Connections that provide sensor values.
905  * @param objectMgrPaths Mappings from connection name to DBus object path that
906  * implements ObjectManager.
907  */
908 void getSensorData(
909     std::shared_ptr<SensorsAsyncResp> SensorsAsyncResp,
910     const std::shared_ptr<boost::container::flat_set<std::string>> sensorNames,
911     const boost::container::flat_set<std::string>& connections,
912     const boost::container::flat_map<std::string, std::string>& objectMgrPaths)
913 {
914     BMCWEB_LOG_DEBUG << "getSensorData enter";
915     // Get managed objects from all services exposing sensors
916     for (const std::string& connection : connections)
917     {
918         // Response handler to process managed objects
919         auto getManagedObjectsCb = [SensorsAsyncResp, sensorNames](
920                                        const boost::system::error_code ec,
921                                        ManagedObjectsVectorType& resp) {
922             BMCWEB_LOG_DEBUG << "getManagedObjectsCb enter";
923             if (ec)
924             {
925                 BMCWEB_LOG_ERROR << "getManagedObjectsCb DBUS error: " << ec;
926                 messages::internalError(SensorsAsyncResp->res);
927                 return;
928             }
929             // Go through all objects and update response with sensor data
930             for (const auto& objDictEntry : resp)
931             {
932                 const std::string& objPath =
933                     static_cast<const std::string&>(objDictEntry.first);
934                 BMCWEB_LOG_DEBUG << "getManagedObjectsCb parsing object "
935                                  << objPath;
936 
937                 std::vector<std::string> split;
938                 // Reserve space for
939                 // /xyz/openbmc_project/sensors/<name>/<subname>
940                 split.reserve(6);
941                 boost::algorithm::split(split, objPath, boost::is_any_of("/"));
942                 if (split.size() < 6)
943                 {
944                     BMCWEB_LOG_ERROR << "Got path that isn't long enough "
945                                      << objPath;
946                     continue;
947                 }
948                 // These indexes aren't intuitive, as boost::split puts an empty
949                 // string at the beginning
950                 const std::string& sensorType = split[4];
951                 const std::string& sensorName = split[5];
952                 BMCWEB_LOG_DEBUG << "sensorName " << sensorName
953                                  << " sensorType " << sensorType;
954                 if (sensorNames->find(objPath) == sensorNames->end())
955                 {
956                     BMCWEB_LOG_ERROR << sensorName << " not in sensor list ";
957                     continue;
958                 }
959 
960                 const char* fieldName = nullptr;
961                 if (sensorType == "temperature")
962                 {
963                     fieldName = "Temperatures";
964                 }
965                 else if (sensorType == "fan" || sensorType == "fan_tach" ||
966                          sensorType == "fan_pwm")
967                 {
968                     fieldName = "Fans";
969                 }
970                 else if (sensorType == "voltage")
971                 {
972                     fieldName = "Voltages";
973                 }
974                 else if (sensorType == "current")
975                 {
976                     fieldName = "PowerSupplies";
977                 }
978                 else if (sensorType == "power")
979                 {
980                     fieldName = "PowerSupplies";
981                 }
982                 else
983                 {
984                     BMCWEB_LOG_ERROR << "Unsure how to handle sensorType "
985                                      << sensorType;
986                     continue;
987                 }
988 
989                 nlohmann::json& tempArray =
990                     SensorsAsyncResp->res.jsonValue[fieldName];
991 
992                 if (fieldName == "PowerSupplies" && !tempArray.empty())
993                 {
994                     // Power supplies put multiple "sensors" into a single power
995                     // supply entry, so only create the first one
996                 }
997                 else
998                 {
999                     tempArray.push_back(
1000                         {{"@odata.id", "/redfish/v1/Chassis/" +
1001                                            SensorsAsyncResp->chassisId + "/" +
1002                                            SensorsAsyncResp->chassisSubNode +
1003                                            "#/" + fieldName + "/"}});
1004                 }
1005                 nlohmann::json& sensorJson = tempArray.back();
1006 
1007                 objectInterfacesToJson(sensorName, sensorType,
1008                                        objDictEntry.second, sensorJson);
1009             }
1010             if (SensorsAsyncResp.use_count() == 1)
1011             {
1012                 sortJSONResponse(SensorsAsyncResp);
1013                 if (SensorsAsyncResp->chassisSubNode == "Thermal")
1014                 {
1015                     populateFanRedundancy(SensorsAsyncResp);
1016                 }
1017             }
1018             BMCWEB_LOG_DEBUG << "getManagedObjectsCb exit";
1019         };
1020 
1021         // Find DBus object path that implements ObjectManager for the current
1022         // connection.  If no mapping found, default to "/".
1023         auto iter = objectMgrPaths.find(connection);
1024         const std::string& objectMgrPath =
1025             (iter != objectMgrPaths.end()) ? iter->second : "/";
1026         BMCWEB_LOG_DEBUG << "ObjectManager path for " << connection << " is "
1027                          << objectMgrPath;
1028 
1029         crow::connections::systemBus->async_method_call(
1030             getManagedObjectsCb, connection, objectMgrPath,
1031             "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
1032     };
1033     BMCWEB_LOG_DEBUG << "getSensorData exit";
1034 }
1035 
1036 /**
1037  * @brief Entry point for retrieving sensors data related to requested
1038  *        chassis.
1039  * @param SensorsAsyncResp   Pointer to object holding response data
1040  */
1041 void getChassisData(std::shared_ptr<SensorsAsyncResp> SensorsAsyncResp)
1042 {
1043     BMCWEB_LOG_DEBUG << "getChassisData enter";
1044     auto getChassisCb =
1045         [SensorsAsyncResp](
1046             std::shared_ptr<boost::container::flat_set<std::string>>
1047                 sensorNames) {
1048             BMCWEB_LOG_DEBUG << "getChassisCb enter";
1049             auto getConnectionCb =
1050                 [SensorsAsyncResp,
1051                  sensorNames](const boost::container::flat_set<std::string>&
1052                                   connections) {
1053                     BMCWEB_LOG_DEBUG << "getConnectionCb enter";
1054                     auto getObjectManagerPathsCb =
1055                         [SensorsAsyncResp, sensorNames, connections](
1056                             const boost::container::flat_map<
1057                                 std::string, std::string>& objectMgrPaths) {
1058                             BMCWEB_LOG_DEBUG << "getObjectManagerPathsCb enter";
1059                             // Get sensor data and store results in JSON
1060                             // response
1061                             getSensorData(SensorsAsyncResp, sensorNames,
1062                                           connections, objectMgrPaths);
1063                             BMCWEB_LOG_DEBUG << "getObjectManagerPathsCb exit";
1064                         };
1065 
1066                     // Get mapping from connection names to the DBus object
1067                     // paths that implement the ObjectManager interface
1068                     getObjectManagerPaths(SensorsAsyncResp,
1069                                           std::move(getObjectManagerPathsCb));
1070                     BMCWEB_LOG_DEBUG << "getConnectionCb exit";
1071                 };
1072 
1073             // Get set of connections that provide sensor values
1074             getConnections(SensorsAsyncResp, sensorNames,
1075                            std::move(getConnectionCb));
1076             BMCWEB_LOG_DEBUG << "getChassisCb exit";
1077         };
1078 
1079 #ifdef BMCWEB_ENABLE_REDFISH_ONE_CHASSIS
1080     // Get all sensor names
1081     getAllSensors(SensorsAsyncResp, std::move(getChassisCb));
1082 #else
1083     // Get sensor names in chassis
1084     getChassis(SensorsAsyncResp, std::move(getChassisCb));
1085 #endif
1086     BMCWEB_LOG_DEBUG << "getChassisData exit";
1087 };
1088 
1089 /**
1090  * @brief Find the requested sensorName in the list of all sensors supplied by
1091  * the chassis node
1092  *
1093  * @param sensorName   The sensor name supplied in the PATCH request
1094  * @param sensorsList  The list of sensors managed by the chassis node
1095  * @param sensorsModified  The list of sensors that were found as a result of
1096  *                         repeated calls to this function
1097  */
1098 bool findSensorNameUsingSensorPath(
1099     const std::string& sensorName,
1100     boost::container::flat_set<std::string>& sensorsList,
1101     boost::container::flat_set<std::string>& sensorsModified)
1102 {
1103     for (const std::string& chassisSensor : sensorsList)
1104     {
1105         std::string thisSensorName;
1106         if (!dbus::utility::getNthStringFromPath(chassisSensor, 5,
1107                                                  thisSensorName))
1108         {
1109             BMCWEB_LOG_ERROR << "Got path that isn't long enough "
1110                              << chassisSensor;
1111             continue;
1112         }
1113         if (thisSensorName == sensorName)
1114         {
1115             sensorsModified.emplace(chassisSensor);
1116             return true;
1117         }
1118     }
1119     return false;
1120 }
1121 
1122 /**
1123  * @brief Entry point for overriding sensor values of given sensor
1124  *
1125  * @param res   response object
1126  * @param req   request object
1127  * @param params   parameter passed for CRUD
1128  * @param typeList   TypeList of sensors for the resource queried
1129  * @param chassisSubNode   Chassis Node for which the query has to happen
1130  */
1131 void setSensorOverride(crow::Response& res, const crow::Request& req,
1132                        const std::vector<std::string>& params,
1133                        const std::initializer_list<const char*> typeList,
1134                        const std::string& chassisSubNode)
1135 {
1136 
1137     // TODO: Need to figure out dynamic way to restrict patch (Set Sensor
1138     // override) based on another d-bus announcement to be more generic.
1139     if (params.size() != 1)
1140     {
1141         messages::internalError(res);
1142         res.end();
1143         return;
1144     }
1145 
1146     std::unordered_map<std::string, std::vector<nlohmann::json>> allCollections;
1147     std::optional<std::vector<nlohmann::json>> temperatureCollections;
1148     std::optional<std::vector<nlohmann::json>> fanCollections;
1149     std::vector<nlohmann::json> voltageCollections;
1150     BMCWEB_LOG_INFO << "setSensorOverride for subNode" << chassisSubNode
1151                     << "\n";
1152 
1153     if (chassisSubNode == "Thermal")
1154     {
1155         if (!json_util::readJson(req, res, "Temperatures",
1156                                  temperatureCollections, "Fans",
1157                                  fanCollections))
1158         {
1159             return;
1160         }
1161         if (!temperatureCollections && !fanCollections)
1162         {
1163             messages::resourceNotFound(res, "Thermal",
1164                                        "Temperatures / Voltages");
1165             res.end();
1166             return;
1167         }
1168         if (temperatureCollections)
1169         {
1170             allCollections.emplace("Temperatures",
1171                                    *std::move(temperatureCollections));
1172         }
1173         if (fanCollections)
1174         {
1175             allCollections.emplace("Fans", *std::move(fanCollections));
1176         }
1177     }
1178     else if (chassisSubNode == "Power")
1179     {
1180         if (!json_util::readJson(req, res, "Voltages", voltageCollections))
1181         {
1182             return;
1183         }
1184         allCollections.emplace("Voltages", std::move(voltageCollections));
1185     }
1186     else
1187     {
1188         res.result(boost::beast::http::status::not_found);
1189         res.end();
1190         return;
1191     }
1192 
1193     const char* propertyValueName;
1194     std::unordered_map<std::string, std::pair<double, std::string>> overrideMap;
1195     std::string memberId;
1196     double value;
1197     for (auto& collectionItems : allCollections)
1198     {
1199         if (collectionItems.first == "Temperatures")
1200         {
1201             propertyValueName = "ReadingCelsius";
1202         }
1203         else if (collectionItems.first == "Fans")
1204         {
1205             propertyValueName = "Reading";
1206         }
1207         else
1208         {
1209             propertyValueName = "ReadingVolts";
1210         }
1211         for (auto& item : collectionItems.second)
1212         {
1213             if (!json_util::readJson(item, res, "MemberId", memberId,
1214                                      propertyValueName, value))
1215             {
1216                 return;
1217             }
1218             overrideMap.emplace(memberId,
1219                                 std::make_pair(value, collectionItems.first));
1220         }
1221     }
1222     const std::string& chassisName = params[0];
1223     auto sensorAsyncResp = std::make_shared<SensorsAsyncResp>(
1224         res, chassisName, typeList, chassisSubNode);
1225     auto getChassisSensorListCb = [sensorAsyncResp,
1226                                    overrideMap](const std::shared_ptr<
1227                                                 boost::container::flat_set<
1228                                                     std::string>>
1229                                                     sensorsList) {
1230         // Match sensor names in the PATCH request to those managed by the
1231         // chassis node
1232         const std::shared_ptr<boost::container::flat_set<std::string>>
1233             sensorNames =
1234                 std::make_shared<boost::container::flat_set<std::string>>();
1235         for (const auto& item : overrideMap)
1236         {
1237             const auto& sensor = item.first;
1238             if (!findSensorNameUsingSensorPath(sensor, *sensorsList,
1239                                                *sensorNames))
1240             {
1241                 BMCWEB_LOG_INFO << "Unable to find memberId " << item.first;
1242                 messages::resourceNotFound(sensorAsyncResp->res,
1243                                            item.second.second, item.first);
1244                 return;
1245             }
1246         }
1247         // Get the connection to which the memberId belongs
1248         auto getObjectsWithConnectionCb =
1249             [sensorAsyncResp, overrideMap](
1250                 const boost::container::flat_set<std::string>& connections,
1251                 const std::set<std::pair<std::string, std::string>>&
1252                     objectsWithConnection) {
1253                 if (objectsWithConnection.size() != overrideMap.size())
1254                 {
1255                     BMCWEB_LOG_INFO
1256                         << "Unable to find all objects with proper connection "
1257                         << objectsWithConnection.size() << " requested "
1258                         << overrideMap.size() << "\n";
1259                     messages::resourceNotFound(
1260                         sensorAsyncResp->res,
1261                         sensorAsyncResp->chassisSubNode == "Thermal"
1262                             ? "Temperatures"
1263                             : "Voltages",
1264                         "Count");
1265                     return;
1266                 }
1267                 for (const auto& item : objectsWithConnection)
1268                 {
1269 
1270                     auto lastPos = item.first.rfind('/');
1271                     if (lastPos == std::string::npos)
1272                     {
1273                         messages::internalError(sensorAsyncResp->res);
1274                         return;
1275                     }
1276                     std::string sensorName = item.first.substr(lastPos + 1);
1277 
1278                     const auto& iterator = overrideMap.find(sensorName);
1279                     if (iterator == overrideMap.end())
1280                     {
1281                         BMCWEB_LOG_INFO << "Unable to find sensor object"
1282                                         << item.first << "\n";
1283                         messages::internalError(sensorAsyncResp->res);
1284                         return;
1285                     }
1286                     crow::connections::systemBus->async_method_call(
1287                         [sensorAsyncResp](const boost::system::error_code ec) {
1288                             if (ec)
1289                             {
1290                                 BMCWEB_LOG_DEBUG
1291                                     << "setOverrideValueStatus DBUS error: "
1292                                     << ec;
1293                                 messages::internalError(sensorAsyncResp->res);
1294                                 return;
1295                             }
1296                         },
1297                         item.second, item.first,
1298                         "org.freedesktop.DBus.Properties", "Set",
1299                         "xyz.openbmc_project.Sensor.Value", "Value",
1300                         sdbusplus::message::variant<double>(
1301                             iterator->second.first));
1302                 }
1303             };
1304         // Get object with connection for the given sensor name
1305         getObjectsWithConnection(sensorAsyncResp, sensorNames,
1306                                  std::move(getObjectsWithConnectionCb));
1307     };
1308     // get full sensor list for the given chassisId and cross verify the sensor.
1309     getChassis(sensorAsyncResp, std::move(getChassisSensorListCb));
1310 }
1311 
1312 } // namespace redfish
1313