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