xref: /openbmc/bmcweb/features/redfish/lib/sensors.hpp (revision f65af9e8c0d28a4b725c0d05ecbeb3007e2a09cf)
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         res.jsonValue["@odata.id"] =
58             "/redfish/v1/Chassis/" + chassisId + "/" + subNode;
59     }
60 
61     ~SensorsAsyncResp()
62     {
63         if (res.result() == boost::beast::http::status::internal_server_error)
64         {
65             // Reset the json object to clear out any data that made it in
66             // before the error happened todo(ed) handle error condition with
67             // proper code
68             res.jsonValue = nlohmann::json::object();
69         }
70         res.end();
71     }
72 
73     crow::Response& res;
74     std::string chassisId{};
75     const std::vector<const char*> types;
76     std::string chassisSubNode{};
77 };
78 
79 /**
80  * @brief Get objects with connection necessary for sensors
81  * @param SensorsAsyncResp Pointer to object holding response data
82  * @param sensorNames Sensors retrieved from chassis
83  * @param callback Callback for processing gathered connections
84  */
85 template <typename Callback>
86 void getObjectsWithConnection(
87     std::shared_ptr<SensorsAsyncResp> SensorsAsyncResp,
88     const boost::container::flat_set<std::string>& sensorNames,
89     Callback&& callback)
90 {
91     BMCWEB_LOG_DEBUG << "getObjectsWithConnection enter";
92     const std::string path = "/xyz/openbmc_project/sensors";
93     const std::array<std::string, 1> interfaces = {
94         "xyz.openbmc_project.Sensor.Value"};
95 
96     // Response handler for parsing objects subtree
97     auto respHandler = [callback{std::move(callback)}, SensorsAsyncResp,
98                         sensorNames](const boost::system::error_code ec,
99                                      const GetSubTreeType& subtree) {
100         BMCWEB_LOG_DEBUG << "getObjectsWithConnection resp_handler enter";
101         if (ec)
102         {
103             messages::internalError(SensorsAsyncResp->res);
104             BMCWEB_LOG_ERROR
105                 << "getObjectsWithConnection resp_handler: Dbus error " << ec;
106             return;
107         }
108 
109         BMCWEB_LOG_DEBUG << "Found " << subtree.size() << " subtrees";
110 
111         // Make unique list of connections only for requested sensor types and
112         // found in the chassis
113         boost::container::flat_set<std::string> connections;
114         std::set<std::pair<std::string, std::string>> objectsWithConnection;
115         // Intrinsic to avoid malloc.  Most systems will have < 8 sensor
116         // producers
117         connections.reserve(8);
118 
119         BMCWEB_LOG_DEBUG << "sensorNames list count: " << sensorNames.size();
120         for (const std::string& tsensor : sensorNames)
121         {
122             BMCWEB_LOG_DEBUG << "Sensor to find: " << tsensor;
123         }
124 
125         for (const std::pair<
126                  std::string,
127                  std::vector<std::pair<std::string, std::vector<std::string>>>>&
128                  object : subtree)
129         {
130             for (const char* type : SensorsAsyncResp->types)
131             {
132                 if (boost::starts_with(object.first, type))
133                 {
134                     auto lastPos = object.first.rfind('/');
135                     if (lastPos != std::string::npos)
136                     {
137                         std::string sensorName =
138                             object.first.substr(lastPos + 1);
139 
140                         if (sensorNames.find(sensorName) != sensorNames.end())
141                         {
142                             // For each Connection name
143                             for (const std::pair<std::string,
144                                                  std::vector<std::string>>&
145                                      objData : object.second)
146                             {
147                                 BMCWEB_LOG_DEBUG << "Adding connection: "
148                                                  << objData.first;
149                                 connections.insert(objData.first);
150                                 objectsWithConnection.insert(std::make_pair(
151                                     object.first, objData.first));
152                             }
153                         }
154                     }
155                     break;
156                 }
157             }
158         }
159         BMCWEB_LOG_DEBUG << "Found " << connections.size() << " connections";
160         callback(std::move(connections), std::move(objectsWithConnection));
161         BMCWEB_LOG_DEBUG << "getObjectsWithConnection resp_handler exit";
162     };
163     // Make call to ObjectMapper to find all sensors objects
164     crow::connections::systemBus->async_method_call(
165         std::move(respHandler), "xyz.openbmc_project.ObjectMapper",
166         "/xyz/openbmc_project/object_mapper",
167         "xyz.openbmc_project.ObjectMapper", "GetSubTree", path, 2, interfaces);
168     BMCWEB_LOG_DEBUG << "getObjectsWithConnection exit";
169 }
170 
171 /**
172  * @brief Create connections necessary for sensors
173  * @param SensorsAsyncResp Pointer to object holding response data
174  * @param sensorNames Sensors retrieved from chassis
175  * @param callback Callback for processing gathered connections
176  */
177 template <typename Callback>
178 void getConnections(std::shared_ptr<SensorsAsyncResp> SensorsAsyncResp,
179                     const boost::container::flat_set<std::string>& sensorNames,
180                     Callback&& callback)
181 {
182     auto objectsWithConnectionCb =
183         [callback](const boost::container::flat_set<std::string>& connections,
184                    const std::set<std::pair<std::string, std::string>>&
185                        objectsWithConnection) {
186             callback(std::move(connections));
187         };
188     getObjectsWithConnection(SensorsAsyncResp, sensorNames,
189                              std::move(objectsWithConnectionCb));
190 }
191 
192 /**
193  * @brief Retrieves requested chassis sensors and redundancy data from DBus .
194  * @param SensorsAsyncResp   Pointer to object holding response data
195  * @param callback  Callback for next step in gathered sensor processing
196  */
197 template <typename Callback>
198 void getChassis(std::shared_ptr<SensorsAsyncResp> SensorsAsyncResp,
199                 Callback&& callback)
200 {
201     BMCWEB_LOG_DEBUG << "getChassis enter";
202     // Process response from EntityManager and extract chassis data
203     auto respHandler = [callback{std::move(callback)},
204                         SensorsAsyncResp](const boost::system::error_code ec,
205                                           ManagedObjectsVectorType& resp) {
206         BMCWEB_LOG_DEBUG << "getChassis respHandler enter";
207         if (ec)
208         {
209             BMCWEB_LOG_ERROR << "getChassis respHandler DBUS error: " << ec;
210             messages::internalError(SensorsAsyncResp->res);
211             return;
212         }
213         boost::container::flat_set<std::string> sensorNames;
214 
215         //   SensorsAsyncResp->chassisId
216         bool foundChassis = false;
217         std::vector<std::string> split;
218         // Reserve space for
219         // /xyz/openbmc_project/inventory/<name>/<subname> + 3 subnames
220         split.reserve(8);
221 
222         for (const auto& objDictEntry : resp)
223         {
224             const std::string& objectPath =
225                 static_cast<const std::string&>(objDictEntry.first);
226             boost::algorithm::split(split, objectPath, boost::is_any_of("/"));
227             if (split.size() < 2)
228             {
229                 BMCWEB_LOG_ERROR << "Got path that isn't long enough "
230                                  << objectPath;
231                 split.clear();
232                 continue;
233             }
234             const std::string& sensorName = split.end()[-1];
235             const std::string& chassisName = split.end()[-2];
236 
237             if (chassisName != SensorsAsyncResp->chassisId)
238             {
239                 split.clear();
240                 continue;
241             }
242             BMCWEB_LOG_DEBUG << "New sensor: " << sensorName;
243             foundChassis = true;
244             sensorNames.emplace(sensorName);
245             split.clear();
246         };
247         BMCWEB_LOG_DEBUG << "Found " << sensorNames.size() << " Sensor names";
248 
249         if (!foundChassis)
250         {
251             BMCWEB_LOG_INFO << "Unable to find chassis named "
252                             << SensorsAsyncResp->chassisId;
253             messages::resourceNotFound(SensorsAsyncResp->res, "Chassis",
254                                        SensorsAsyncResp->chassisId);
255         }
256         else
257         {
258             callback(sensorNames);
259         }
260         BMCWEB_LOG_DEBUG << "getChassis respHandler exit";
261     };
262 
263     // Make call to EntityManager to find all chassis objects
264     crow::connections::systemBus->async_method_call(
265         respHandler, "xyz.openbmc_project.EntityManager", "/",
266         "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
267     BMCWEB_LOG_DEBUG << "getChassis exit";
268 }
269 
270 /**
271  * @brief Builds a json sensor representation of a sensor.
272  * @param sensorName  The name of the sensor to be built
273  * @param sensorType  The type (temperature, fan_tach, etc) of the sensor to
274  * build
275  * @param interfacesDict  A dictionary of the interfaces and properties of said
276  * interfaces to be built from
277  * @param sensor_json  The json object to fill
278  */
279 void objectInterfacesToJson(
280     const std::string& sensorName, const std::string& sensorType,
281     const boost::container::flat_map<
282         std::string, boost::container::flat_map<std::string, SensorVariant>>&
283         interfacesDict,
284     nlohmann::json& sensor_json)
285 {
286     // We need a value interface before we can do anything with it
287     auto valueIt = interfacesDict.find("xyz.openbmc_project.Sensor.Value");
288     if (valueIt == interfacesDict.end())
289     {
290         BMCWEB_LOG_ERROR << "Sensor doesn't have a value interface";
291         return;
292     }
293 
294     // Assume values exist as is (10^0 == 1) if no scale exists
295     int64_t scaleMultiplier = 0;
296 
297     auto scaleIt = valueIt->second.find("Scale");
298     // If a scale exists, pull value as int64, and use the scaling.
299     if (scaleIt != valueIt->second.end())
300     {
301         const int64_t* int64Value = std::get_if<int64_t>(&scaleIt->second);
302         if (int64Value != nullptr)
303         {
304             scaleMultiplier = *int64Value;
305         }
306     }
307 
308     sensor_json["MemberId"] = sensorName;
309     sensor_json["Name"] = sensorName;
310     sensor_json["Status"]["State"] = "Enabled";
311     sensor_json["Status"]["Health"] = "OK";
312 
313     // Parameter to set to override the type we get from dbus, and force it to
314     // int, regardless of what is available.  This is used for schemas like fan,
315     // that require integers, not floats.
316     bool forceToInt = false;
317 
318     const char* unit = "Reading";
319     if (sensorType == "temperature")
320     {
321         unit = "ReadingCelsius";
322         sensor_json["@odata.type"] = "#Thermal.v1_3_0.Temperature";
323         // TODO(ed) Documentation says that path should be type fan_tach,
324         // implementation seems to implement fan
325     }
326     else if (sensorType == "fan" || sensorType == "fan_tach")
327     {
328         unit = "Reading";
329         sensor_json["ReadingUnits"] = "RPM";
330         sensor_json["@odata.type"] = "#Thermal.v1_3_0.Fan";
331         forceToInt = true;
332     }
333     else if (sensorType == "fan_pwm")
334     {
335         unit = "Reading";
336         sensor_json["ReadingUnits"] = "Percent";
337         sensor_json["@odata.type"] = "#Thermal.v1_3_0.Fan";
338         forceToInt = true;
339     }
340     else if (sensorType == "voltage")
341     {
342         unit = "ReadingVolts";
343         sensor_json["@odata.type"] = "#Power.v1_0_0.Voltage";
344     }
345     else if (sensorType == "power")
346     {
347         unit = "LastPowerOutputWatts";
348     }
349     else
350     {
351         BMCWEB_LOG_ERROR << "Redfish cannot map object type for " << sensorName;
352         return;
353     }
354     // Map of dbus interface name, dbus property name and redfish property_name
355     std::vector<std::tuple<const char*, const char*, const char*>> properties;
356     properties.reserve(7);
357 
358     properties.emplace_back("xyz.openbmc_project.Sensor.Value", "Value", unit);
359     properties.emplace_back("xyz.openbmc_project.Sensor.Threshold.Warning",
360                             "WarningHigh", "UpperThresholdNonCritical");
361     properties.emplace_back("xyz.openbmc_project.Sensor.Threshold.Warning",
362                             "WarningLow", "LowerThresholdNonCritical");
363     properties.emplace_back("xyz.openbmc_project.Sensor.Threshold.Critical",
364                             "CriticalHigh", "UpperThresholdCritical");
365     properties.emplace_back("xyz.openbmc_project.Sensor.Threshold.Critical",
366                             "CriticalLow", "LowerThresholdCritical");
367 
368     // TODO Need to get UpperThresholdFatal and LowerThresholdFatal
369 
370     if (sensorType == "temperature")
371     {
372         properties.emplace_back("xyz.openbmc_project.Sensor.Value", "MinValue",
373                                 "MinReadingRangeTemp");
374         properties.emplace_back("xyz.openbmc_project.Sensor.Value", "MaxValue",
375                                 "MaxReadingRangeTemp");
376     }
377     else
378     {
379         properties.emplace_back("xyz.openbmc_project.Sensor.Value", "MinValue",
380                                 "MinReadingRange");
381         properties.emplace_back("xyz.openbmc_project.Sensor.Value", "MaxValue",
382                                 "MaxReadingRange");
383     }
384 
385     for (const std::tuple<const char*, const char*, const char*>& p :
386          properties)
387     {
388         auto interfaceProperties = interfacesDict.find(std::get<0>(p));
389         if (interfaceProperties != interfacesDict.end())
390         {
391             auto valueIt = interfaceProperties->second.find(std::get<1>(p));
392             if (valueIt != interfaceProperties->second.end())
393             {
394                 const SensorVariant& valueVariant = valueIt->second;
395                 nlohmann::json& valueIt = sensor_json[std::get<2>(p)];
396                 // Attempt to pull the int64 directly
397                 const int64_t* int64Value = std::get_if<int64_t>(&valueVariant);
398 
399                 const double* doubleValue = std::get_if<double>(&valueVariant);
400                 double temp = 0.0;
401                 if (int64Value != nullptr)
402                 {
403                     temp = *int64Value;
404                 }
405                 else if (doubleValue != nullptr)
406                 {
407                     temp = *doubleValue;
408                 }
409                 else
410                 {
411                     BMCWEB_LOG_ERROR
412                         << "Got value interface that wasn't int or double";
413                     continue;
414                 }
415                 temp = temp * std::pow(10, scaleMultiplier);
416                 if (forceToInt)
417                 {
418                     valueIt = static_cast<int64_t>(temp);
419                 }
420                 else
421                 {
422                     valueIt = temp;
423                 }
424             }
425         }
426     }
427     BMCWEB_LOG_DEBUG << "Added sensor " << sensorName;
428 }
429 
430 /**
431  * @brief Entry point for retrieving sensors data related to requested
432  *        chassis.
433  * @param SensorsAsyncResp   Pointer to object holding response data
434  */
435 void getChassisData(std::shared_ptr<SensorsAsyncResp> SensorsAsyncResp)
436 {
437     BMCWEB_LOG_DEBUG << "getChassisData enter";
438     auto getChassisCb = [&, SensorsAsyncResp](
439                             boost::container::flat_set<std::string>&
440                                 sensorNames) {
441         BMCWEB_LOG_DEBUG << "getChassisCb enter";
442         auto getConnectionCb =
443             [&, SensorsAsyncResp, sensorNames](
444                 const boost::container::flat_set<std::string>& connections) {
445                 BMCWEB_LOG_DEBUG << "getConnectionCb enter";
446                 // Get managed objects from all services exposing sensors
447                 for (const std::string& connection : connections)
448                 {
449                     // Response handler to process managed objects
450                     auto getManagedObjectsCb =
451                         [&, SensorsAsyncResp,
452                          sensorNames](const boost::system::error_code ec,
453                                       ManagedObjectsVectorType& resp) {
454                             BMCWEB_LOG_DEBUG << "getManagedObjectsCb enter";
455                             if (ec)
456                             {
457                                 BMCWEB_LOG_ERROR
458                                     << "getManagedObjectsCb DBUS error: " << ec;
459                                 messages::internalError(SensorsAsyncResp->res);
460                                 return;
461                             }
462                             // Go through all objects and update response with
463                             // sensor data
464                             for (const auto& objDictEntry : resp)
465                             {
466                                 const std::string& objPath =
467                                     static_cast<const std::string&>(
468                                         objDictEntry.first);
469                                 BMCWEB_LOG_DEBUG
470                                     << "getManagedObjectsCb parsing object "
471                                     << objPath;
472 
473                                 std::vector<std::string> split;
474                                 // Reserve space for
475                                 // /xyz/openbmc_project/sensors/<name>/<subname>
476                                 split.reserve(6);
477                                 boost::algorithm::split(split, objPath,
478                                                         boost::is_any_of("/"));
479                                 if (split.size() < 6)
480                                 {
481                                     BMCWEB_LOG_ERROR
482                                         << "Got path that isn't long enough "
483                                         << objPath;
484                                     continue;
485                                 }
486                                 // These indexes aren't intuitive, as
487                                 // boost::split puts an empty string at the
488                                 // beggining
489                                 const std::string& sensorType = split[4];
490                                 const std::string& sensorName = split[5];
491                                 BMCWEB_LOG_DEBUG << "sensorName " << sensorName
492                                                  << " sensorType "
493                                                  << sensorType;
494                                 if (sensorNames.find(sensorName) ==
495                                     sensorNames.end())
496                                 {
497                                     BMCWEB_LOG_ERROR << sensorName
498                                                      << " not in sensor list ";
499                                     continue;
500                                 }
501 
502                                 const char* fieldName = nullptr;
503                                 if (sensorType == "temperature")
504                                 {
505                                     fieldName = "Temperatures";
506                                 }
507                                 else if (sensorType == "fan" ||
508                                          sensorType == "fan_tach" ||
509                                          sensorType == "fan_pwm")
510                                 {
511                                     fieldName = "Fans";
512                                 }
513                                 else if (sensorType == "voltage")
514                                 {
515                                     fieldName = "Voltages";
516                                 }
517                                 else if (sensorType == "current")
518                                 {
519                                     fieldName = "PowerSupply";
520                                 }
521                                 else if (sensorType == "power")
522                                 {
523                                     fieldName = "PowerSupply";
524                                 }
525                                 else
526                                 {
527                                     BMCWEB_LOG_ERROR
528                                         << "Unsure how to handle sensorType "
529                                         << sensorType;
530                                     continue;
531                                 }
532 
533                                 nlohmann::json& tempArray =
534                                     SensorsAsyncResp->res.jsonValue[fieldName];
535 
536                                 tempArray.push_back(
537                                     {{"@odata.id",
538                                       "/redfish/v1/Chassis/" +
539                                           SensorsAsyncResp->chassisId + "/" +
540                                           SensorsAsyncResp->chassisSubNode +
541                                           "#/" + fieldName + "/" +
542                                           std::to_string(tempArray.size())}});
543                                 nlohmann::json& sensorJson = tempArray.back();
544 
545                                 objectInterfacesToJson(sensorName, sensorType,
546                                                        objDictEntry.second,
547                                                        sensorJson);
548                             }
549                             BMCWEB_LOG_DEBUG << "getManagedObjectsCb exit";
550                         };
551                     crow::connections::systemBus->async_method_call(
552                         getManagedObjectsCb, connection, "/",
553                         "org.freedesktop.DBus.ObjectManager",
554                         "GetManagedObjects");
555                 };
556                 BMCWEB_LOG_DEBUG << "getConnectionCb exit";
557             };
558         // get connections and then pass it to get sensors
559         getConnections(SensorsAsyncResp, sensorNames,
560                        std::move(getConnectionCb));
561         BMCWEB_LOG_DEBUG << "getChassisCb exit";
562     };
563 
564     // get chassis information related to sensors
565     getChassis(SensorsAsyncResp, std::move(getChassisCb));
566     BMCWEB_LOG_DEBUG << "getChassisData exit";
567 };
568 
569 /**
570  * @brief Entry point for overriding sensor values of given sensor
571  *
572  * @param res   response object
573  * @param req   request object
574  * @param params   parameter passed for CRUD
575  * @param typeList   TypeList of sensors for the resource queried
576  * @param chassisSubNode   Chassis Node for which the query has to happen
577  */
578 void setSensorOverride(crow::Response& res, const crow::Request& req,
579                        const std::vector<std::string>& params,
580                        const std::initializer_list<const char*> typeList,
581                        const std::string& chassisSubNode)
582 {
583 
584     // TODO: Need to figure out dynamic way to restrict patch (Set Sensor
585     // override) based on another d-bus announcement to be more generic.
586     if (params.size() != 1)
587     {
588         messages::internalError(res);
589         res.end();
590         return;
591     }
592 
593     std::unordered_map<std::string, std::vector<nlohmann::json>> allCollections;
594     std::optional<std::vector<nlohmann::json>> temperatureCollections;
595     std::optional<std::vector<nlohmann::json>> fanCollections;
596     std::vector<nlohmann::json> voltageCollections;
597     BMCWEB_LOG_INFO << "setSensorOverride for subNode" << chassisSubNode
598                     << "\n";
599 
600     if (chassisSubNode == "Thermal")
601     {
602         if (!json_util::readJson(req, res, "Temperatures",
603                                  temperatureCollections, "Fans",
604                                  fanCollections))
605         {
606             return;
607         }
608         if (!temperatureCollections && !fanCollections)
609         {
610             messages::resourceNotFound(res, "Thermal",
611                                        "Temperatures / Voltages");
612             res.end();
613             return;
614         }
615         if (temperatureCollections)
616         {
617             allCollections.emplace("Temperatures",
618                                    *std::move(temperatureCollections));
619         }
620         if (fanCollections)
621         {
622             allCollections.emplace("Fans", *std::move(fanCollections));
623         }
624     }
625     else if (chassisSubNode == "Power")
626     {
627         if (!json_util::readJson(req, res, "Voltages", voltageCollections))
628         {
629             return;
630         }
631         allCollections.emplace("Voltages", std::move(voltageCollections));
632     }
633     else
634     {
635         res.result(boost::beast::http::status::not_found);
636         res.end();
637         return;
638     }
639 
640     const char* propertyValueName;
641     std::unordered_map<std::string, std::pair<double, std::string>> overrideMap;
642     std::string memberId;
643     double value;
644     for (auto& collectionItems : allCollections)
645     {
646         if (collectionItems.first == "Temperatures")
647         {
648             propertyValueName = "ReadingCelsius";
649         }
650         else if (collectionItems.first == "Fans")
651         {
652             propertyValueName = "Reading";
653         }
654         else
655         {
656             propertyValueName = "ReadingVolts";
657         }
658         for (auto& item : collectionItems.second)
659         {
660             if (!json_util::readJson(item, res, "MemberId", memberId,
661                                      propertyValueName, value))
662             {
663                 return;
664             }
665             overrideMap.emplace(memberId,
666                                 std::make_pair(value, collectionItems.first));
667         }
668     }
669     const std::string& chassisName = params[0];
670     auto sensorAsyncResp = std::make_shared<SensorsAsyncResp>(
671         res, chassisName, typeList, chassisSubNode);
672     // first check for valid chassis id & sensor in requested chassis.
673     auto getChassisSensorListCb = [sensorAsyncResp, overrideMap](
674                                       const boost::container::flat_set<
675                                           std::string>& sensorLists) {
676         boost::container::flat_set<std::string> sensorNames;
677         for (const auto& item : overrideMap)
678         {
679             const auto& sensor = item.first;
680             if (sensorLists.find(item.first) == sensorLists.end())
681             {
682                 BMCWEB_LOG_INFO << "Unable to find memberId " << item.first;
683                 messages::resourceNotFound(sensorAsyncResp->res,
684                                            item.second.second, item.first);
685                 return;
686             }
687             sensorNames.emplace(sensor);
688         }
689         // Get the connection to which the memberId belongs
690         auto getObjectsWithConnectionCb =
691             [sensorAsyncResp, overrideMap](
692                 const boost::container::flat_set<std::string>& connections,
693                 const std::set<std::pair<std::string, std::string>>&
694                     objectsWithConnection) {
695                 if (objectsWithConnection.size() != overrideMap.size())
696                 {
697                     BMCWEB_LOG_INFO
698                         << "Unable to find all objects with proper connection "
699                         << objectsWithConnection.size() << " requested "
700                         << overrideMap.size() << "\n";
701                     messages::resourceNotFound(
702                         sensorAsyncResp->res,
703                         sensorAsyncResp->chassisSubNode == "Thermal"
704                             ? "Temperatures"
705                             : "Voltages",
706                         "Count");
707                     return;
708                 }
709                 for (const auto& item : objectsWithConnection)
710                 {
711 
712                     auto lastPos = item.first.rfind('/');
713                     if (lastPos == std::string::npos)
714                     {
715                         messages::internalError(sensorAsyncResp->res);
716                         return;
717                     }
718                     std::string sensorName = item.first.substr(lastPos + 1);
719 
720                     const auto& iterator = overrideMap.find(sensorName);
721                     if (iterator == overrideMap.end())
722                     {
723                         BMCWEB_LOG_INFO << "Unable to find sensor object"
724                                         << item.first << "\n";
725                         messages::internalError(sensorAsyncResp->res);
726                         return;
727                     }
728                     crow::connections::systemBus->async_method_call(
729                         [sensorAsyncResp](const boost::system::error_code ec) {
730                             if (ec)
731                             {
732                                 BMCWEB_LOG_DEBUG
733                                     << "setOverrideValueStatus DBUS error: "
734                                     << ec;
735                                 messages::internalError(sensorAsyncResp->res);
736                                 return;
737                             }
738                         },
739                         item.second, item.first,
740                         "org.freedesktop.DBus.Properties", "Set",
741                         "xyz.openbmc_project.Sensor.Value", "Value",
742                         sdbusplus::message::variant<double>(
743                             iterator->second.first));
744                 }
745             };
746         // Get object with connection for the given sensor name
747         getObjectsWithConnection(sensorAsyncResp, sensorNames,
748                                  std::move(getObjectsWithConnectionCb));
749     };
750     // get full sensor list for the given chassisId and cross verify the sensor.
751     getChassis(sensorAsyncResp, std::move(getChassisSensorListCb));
752 }
753 
754 } // namespace redfish
755