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 std::sort(entry->begin(), entry->end(),
715 [](const nlohmann::json& c1, const nlohmann::json& c2) {
716 return c1["Name"] < c2["Name"];
717 });
718
719 // add the index counts to the end of each entry
720 size_t count = 0;
721 for (nlohmann::json& sensorJson : *entry)
722 {
723 nlohmann::json::iterator odata = sensorJson.find("@odata.id");
724 if (odata == sensorJson.end())
725 {
726 continue;
727 }
728 std::string* value = odata->get_ptr<std::string*>();
729 if (value != nullptr)
730 {
731 *value += "/" + std::to_string(count);
732 sensorJson["MemberId"] = std::to_string(count);
733 count++;
734 sensorsAsyncResp->updateUri(sensorJson["Name"], *value);
735 }
736 }
737 }
738 }
739 }
740
741 /**
742 * @brief Finds the inventory item with the specified object path.
743 * @param inventoryItems D-Bus inventory items associated with sensors.
744 * @param invItemObjPath D-Bus object path of inventory item.
745 * @return Inventory item within vector, or nullptr if no match found.
746 */
findInventoryItem(const std::shared_ptr<std::vector<InventoryItem>> & inventoryItems,const std::string & invItemObjPath)747 inline InventoryItem* findInventoryItem(
748 const std::shared_ptr<std::vector<InventoryItem>>& inventoryItems,
749 const std::string& invItemObjPath)
750 {
751 for (InventoryItem& inventoryItem : *inventoryItems)
752 {
753 if (inventoryItem.objectPath == invItemObjPath)
754 {
755 return &inventoryItem;
756 }
757 }
758 return nullptr;
759 }
760
761 /**
762 * @brief Finds the inventory item associated with the specified sensor.
763 * @param inventoryItems D-Bus inventory items associated with sensors.
764 * @param sensorObjPath D-Bus object path of sensor.
765 * @return Inventory item within vector, or nullptr if no match found.
766 */
findInventoryItemForSensor(const std::shared_ptr<std::vector<InventoryItem>> & inventoryItems,const std::string & sensorObjPath)767 inline InventoryItem* findInventoryItemForSensor(
768 const std::shared_ptr<std::vector<InventoryItem>>& inventoryItems,
769 const std::string& sensorObjPath)
770 {
771 for (InventoryItem& inventoryItem : *inventoryItems)
772 {
773 if (inventoryItem.sensors.contains(sensorObjPath))
774 {
775 return &inventoryItem;
776 }
777 }
778 return nullptr;
779 }
780
781 /**
782 * @brief Finds the inventory item associated with the specified led path.
783 * @param inventoryItems D-Bus inventory items associated with sensors.
784 * @param ledObjPath D-Bus object path of led.
785 * @return Inventory item within vector, or nullptr if no match found.
786 */
findInventoryItemForLed(std::vector<InventoryItem> & inventoryItems,const std::string & ledObjPath)787 inline InventoryItem* findInventoryItemForLed(
788 std::vector<InventoryItem>& inventoryItems, const std::string& ledObjPath)
789 {
790 for (InventoryItem& inventoryItem : inventoryItems)
791 {
792 if (inventoryItem.ledObjectPath == ledObjPath)
793 {
794 return &inventoryItem;
795 }
796 }
797 return nullptr;
798 }
799
800 /**
801 * @brief Adds inventory item and associated sensor to specified vector.
802 *
803 * Adds a new InventoryItem to the vector if necessary. Searches for an
804 * existing InventoryItem with the specified object path. If not found, one is
805 * added to the vector.
806 *
807 * Next, the specified sensor is added to the set of sensors associated with the
808 * InventoryItem.
809 *
810 * @param inventoryItems D-Bus inventory items associated with sensors.
811 * @param invItemObjPath D-Bus object path of inventory item.
812 * @param sensorObjPath D-Bus object path of sensor
813 */
addInventoryItem(const std::shared_ptr<std::vector<InventoryItem>> & inventoryItems,const std::string & invItemObjPath,const std::string & sensorObjPath)814 inline void addInventoryItem(
815 const std::shared_ptr<std::vector<InventoryItem>>& inventoryItems,
816 const std::string& invItemObjPath, const std::string& sensorObjPath)
817 {
818 // Look for inventory item in vector
819 InventoryItem* inventoryItem =
820 findInventoryItem(inventoryItems, invItemObjPath);
821
822 // If inventory item doesn't exist in vector, add it
823 if (inventoryItem == nullptr)
824 {
825 inventoryItems->emplace_back(invItemObjPath);
826 inventoryItem = &(inventoryItems->back());
827 }
828
829 // Add sensor to set of sensors associated with inventory item
830 inventoryItem->sensors.emplace(sensorObjPath);
831 }
832
833 /**
834 * @brief Stores D-Bus data in the specified inventory item.
835 *
836 * Finds D-Bus data in the specified map of interfaces. Stores the data in the
837 * specified InventoryItem.
838 *
839 * This data is later used to provide sensor property values in the JSON
840 * response.
841 *
842 * @param inventoryItem Inventory item where data will be stored.
843 * @param interfacesDict Map containing D-Bus interfaces and their properties
844 * for the specified inventory item.
845 */
storeInventoryItemData(InventoryItem & inventoryItem,const dbus::utility::DBusInterfacesMap & interfacesDict)846 inline void storeInventoryItemData(
847 InventoryItem& inventoryItem,
848 const dbus::utility::DBusInterfacesMap& interfacesDict)
849 {
850 // Get properties from Inventory.Item interface
851
852 for (const auto& [interface, values] : interfacesDict)
853 {
854 if (interface == "xyz.openbmc_project.Inventory.Item")
855 {
856 for (const auto& [name, dbusValue] : values)
857 {
858 if (name == "Present")
859 {
860 const bool* value = std::get_if<bool>(&dbusValue);
861 if (value != nullptr)
862 {
863 inventoryItem.isPresent = *value;
864 }
865 }
866 }
867 }
868 // Check if Inventory.Item.PowerSupply interface is present
869
870 if (interface == "xyz.openbmc_project.Inventory.Item.PowerSupply")
871 {
872 inventoryItem.isPowerSupply = true;
873 }
874
875 // Get properties from Inventory.Decorator.Asset interface
876 if (interface == "xyz.openbmc_project.Inventory.Decorator.Asset")
877 {
878 for (const auto& [name, dbusValue] : values)
879 {
880 if (name == "Manufacturer")
881 {
882 const std::string* value =
883 std::get_if<std::string>(&dbusValue);
884 if (value != nullptr)
885 {
886 inventoryItem.manufacturer = *value;
887 }
888 }
889 if (name == "Model")
890 {
891 const std::string* value =
892 std::get_if<std::string>(&dbusValue);
893 if (value != nullptr)
894 {
895 inventoryItem.model = *value;
896 }
897 }
898 if (name == "SerialNumber")
899 {
900 const std::string* value =
901 std::get_if<std::string>(&dbusValue);
902 if (value != nullptr)
903 {
904 inventoryItem.serialNumber = *value;
905 }
906 }
907 if (name == "PartNumber")
908 {
909 const std::string* value =
910 std::get_if<std::string>(&dbusValue);
911 if (value != nullptr)
912 {
913 inventoryItem.partNumber = *value;
914 }
915 }
916 }
917 }
918
919 if (interface ==
920 "xyz.openbmc_project.State.Decorator.OperationalStatus")
921 {
922 for (const auto& [name, dbusValue] : values)
923 {
924 if (name == "Functional")
925 {
926 const bool* value = std::get_if<bool>(&dbusValue);
927 if (value != nullptr)
928 {
929 inventoryItem.isFunctional = *value;
930 }
931 }
932 }
933 }
934 }
935 }
936
937 /**
938 * @brief Gets D-Bus data for inventory items associated with sensors.
939 *
940 * Uses the specified connections (services) to obtain D-Bus data for inventory
941 * items associated with sensors. Stores the resulting data in the
942 * inventoryItems vector.
943 *
944 * This data is later used to provide sensor property values in the JSON
945 * response.
946 *
947 * Finds the inventory item data asynchronously. Invokes callback when data has
948 * been obtained.
949 *
950 * The callback must have the following signature:
951 * @code
952 * callback(void)
953 * @endcode
954 *
955 * This function is called recursively, obtaining data asynchronously from one
956 * connection in each call. This ensures the callback is not invoked until the
957 * last asynchronous function has completed.
958 *
959 * @param sensorsAsyncResp Pointer to object holding response data.
960 * @param inventoryItems D-Bus inventory items associated with sensors.
961 * @param invConnections Connections that provide data for the inventory items.
962 * implements ObjectManager.
963 * @param callback Callback to invoke when inventory data has been obtained.
964 * @param invConnectionsIndex Current index in invConnections. Only specified
965 * in recursive calls to this function.
966 */
967 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)968 void getInventoryItemsData(
969 std::shared_ptr<SensorsAsyncResp> sensorsAsyncResp,
970 std::shared_ptr<std::vector<InventoryItem>> inventoryItems,
971 std::shared_ptr<std::set<std::string>> invConnections, Callback&& callback,
972 size_t invConnectionsIndex = 0)
973 {
974 BMCWEB_LOG_DEBUG("getInventoryItemsData enter");
975
976 // If no more connections left, call callback
977 if (invConnectionsIndex >= invConnections->size())
978 {
979 callback();
980 BMCWEB_LOG_DEBUG("getInventoryItemsData exit");
981 return;
982 }
983
984 // Get inventory item data from current connection
985 auto it = invConnections->begin();
986 std::advance(it, invConnectionsIndex);
987 if (it != invConnections->end())
988 {
989 const std::string& invConnection = *it;
990
991 // Get all object paths and their interfaces for current connection
992 sdbusplus::message::object_path path("/xyz/openbmc_project/inventory");
993 dbus::utility::getManagedObjects(
994 invConnection, path,
995 [sensorsAsyncResp, inventoryItems, invConnections,
996 callback = std::forward<Callback>(callback), invConnectionsIndex](
997 const boost::system::error_code& ec,
998 const dbus::utility::ManagedObjectType& resp) mutable {
999 BMCWEB_LOG_DEBUG("getInventoryItemsData respHandler enter");
1000 if (ec)
1001 {
1002 BMCWEB_LOG_ERROR(
1003 "getInventoryItemsData respHandler DBus error {}", ec);
1004 messages::internalError(sensorsAsyncResp->asyncResp->res);
1005 return;
1006 }
1007
1008 // Loop through returned object paths
1009 for (const auto& objDictEntry : resp)
1010 {
1011 const std::string& objPath =
1012 static_cast<const std::string&>(objDictEntry.first);
1013
1014 // If this object path is one of the specified inventory
1015 // items
1016 InventoryItem* inventoryItem =
1017 findInventoryItem(inventoryItems, objPath);
1018 if (inventoryItem != nullptr)
1019 {
1020 // Store inventory data in InventoryItem
1021 storeInventoryItemData(*inventoryItem,
1022 objDictEntry.second);
1023 }
1024 }
1025
1026 // Recurse to get inventory item data from next connection
1027 getInventoryItemsData(sensorsAsyncResp, inventoryItems,
1028 invConnections, std::move(callback),
1029 invConnectionsIndex + 1);
1030
1031 BMCWEB_LOG_DEBUG("getInventoryItemsData respHandler exit");
1032 });
1033 }
1034
1035 BMCWEB_LOG_DEBUG("getInventoryItemsData exit");
1036 }
1037
1038 /**
1039 * @brief Gets connections that provide D-Bus data for inventory items.
1040 *
1041 * Gets the D-Bus connections (services) that provide data for the inventory
1042 * items that are associated with sensors.
1043 *
1044 * Finds the connections asynchronously. Invokes callback when information has
1045 * been obtained.
1046 *
1047 * The callback must have the following signature:
1048 * @code
1049 * callback(std::shared_ptr<std::set<std::string>> invConnections)
1050 * @endcode
1051 *
1052 * @param sensorsAsyncResp Pointer to object holding response data.
1053 * @param inventoryItems D-Bus inventory items associated with sensors.
1054 * @param callback Callback to invoke when connections have been obtained.
1055 */
1056 template <typename Callback>
getInventoryItemsConnections(const std::shared_ptr<SensorsAsyncResp> & sensorsAsyncResp,const std::shared_ptr<std::vector<InventoryItem>> & inventoryItems,Callback && callback)1057 void getInventoryItemsConnections(
1058 const std::shared_ptr<SensorsAsyncResp>& sensorsAsyncResp,
1059 const std::shared_ptr<std::vector<InventoryItem>>& inventoryItems,
1060 Callback&& callback)
1061 {
1062 BMCWEB_LOG_DEBUG("getInventoryItemsConnections enter");
1063
1064 const std::string path = "/xyz/openbmc_project/inventory";
1065 constexpr std::array<std::string_view, 4> interfaces = {
1066 "xyz.openbmc_project.Inventory.Item",
1067 "xyz.openbmc_project.Inventory.Item.PowerSupply",
1068 "xyz.openbmc_project.Inventory.Decorator.Asset",
1069 "xyz.openbmc_project.State.Decorator.OperationalStatus"};
1070
1071 // Make call to ObjectMapper to find all inventory items
1072 dbus::utility::getSubTree(
1073 path, 0, interfaces,
1074 [callback = std::forward<Callback>(callback), sensorsAsyncResp,
1075 inventoryItems](
1076 const boost::system::error_code& ec,
1077 const dbus::utility::MapperGetSubTreeResponse& subtree) mutable {
1078 // Response handler for parsing output from GetSubTree
1079 BMCWEB_LOG_DEBUG("getInventoryItemsConnections respHandler enter");
1080 if (ec)
1081 {
1082 messages::internalError(sensorsAsyncResp->asyncResp->res);
1083 BMCWEB_LOG_ERROR(
1084 "getInventoryItemsConnections respHandler DBus error {}",
1085 ec);
1086 return;
1087 }
1088
1089 // Make unique list of connections for desired inventory items
1090 std::shared_ptr<std::set<std::string>> invConnections =
1091 std::make_shared<std::set<std::string>>();
1092
1093 // Loop through objects from GetSubTree
1094 for (const std::pair<std::string,
1095 std::vector<std::pair<
1096 std::string, std::vector<std::string>>>>&
1097 object : subtree)
1098 {
1099 // Check if object path is one of the specified inventory items
1100 const std::string& objPath = object.first;
1101 if (findInventoryItem(inventoryItems, objPath) != nullptr)
1102 {
1103 // Store all connections to inventory item
1104 for (const std::pair<std::string, std::vector<std::string>>&
1105 objData : object.second)
1106 {
1107 const std::string& invConnection = objData.first;
1108 invConnections->insert(invConnection);
1109 }
1110 }
1111 }
1112
1113 callback(invConnections);
1114 BMCWEB_LOG_DEBUG("getInventoryItemsConnections respHandler exit");
1115 });
1116 BMCWEB_LOG_DEBUG("getInventoryItemsConnections exit");
1117 }
1118
1119 /**
1120 * @brief Gets associations from sensors to inventory items.
1121 *
1122 * Looks for ObjectMapper associations from the specified sensors to related
1123 * inventory items. Then finds the associations from those inventory items to
1124 * their LEDs, if any.
1125 *
1126 * Finds the inventory items asynchronously. Invokes callback when information
1127 * has been obtained.
1128 *
1129 * The callback must have the following signature:
1130 * @code
1131 * callback(std::shared_ptr<std::vector<InventoryItem>> inventoryItems)
1132 * @endcode
1133 *
1134 * @param sensorsAsyncResp Pointer to object holding response data.
1135 * @param sensorNames All sensors within the current chassis.
1136 * implements ObjectManager.
1137 * @param callback Callback to invoke when inventory items have been obtained.
1138 */
1139 template <typename Callback>
getInventoryItemAssociations(const std::shared_ptr<SensorsAsyncResp> & sensorsAsyncResp,const std::shared_ptr<std::set<std::string>> & sensorNames,Callback && callback)1140 void getInventoryItemAssociations(
1141 const std::shared_ptr<SensorsAsyncResp>& sensorsAsyncResp,
1142 const std::shared_ptr<std::set<std::string>>& sensorNames,
1143 Callback&& callback)
1144 {
1145 BMCWEB_LOG_DEBUG("getInventoryItemAssociations enter");
1146
1147 // Call GetManagedObjects on the ObjectMapper to get all associations
1148 sdbusplus::message::object_path path("/");
1149 dbus::utility::getManagedObjects(
1150 "xyz.openbmc_project.ObjectMapper", path,
1151 [callback = std::forward<Callback>(callback), sensorsAsyncResp,
1152 sensorNames](const boost::system::error_code& ec,
1153 const dbus::utility::ManagedObjectType& resp) mutable {
1154 BMCWEB_LOG_DEBUG("getInventoryItemAssociations respHandler enter");
1155 if (ec)
1156 {
1157 BMCWEB_LOG_ERROR(
1158 "getInventoryItemAssociations respHandler DBus error {}",
1159 ec);
1160 messages::internalError(sensorsAsyncResp->asyncResp->res);
1161 return;
1162 }
1163
1164 // Create vector to hold list of inventory items
1165 std::shared_ptr<std::vector<InventoryItem>> inventoryItems =
1166 std::make_shared<std::vector<InventoryItem>>();
1167
1168 // Loop through returned object paths
1169 std::string sensorAssocPath;
1170 sensorAssocPath.reserve(128); // avoid memory allocations
1171 for (const auto& objDictEntry : resp)
1172 {
1173 const std::string& objPath =
1174 static_cast<const std::string&>(objDictEntry.first);
1175
1176 // If path is inventory association for one of the specified
1177 // sensors
1178 for (const std::string& sensorName : *sensorNames)
1179 {
1180 sensorAssocPath = sensorName;
1181 sensorAssocPath += "/inventory";
1182 if (objPath == sensorAssocPath)
1183 {
1184 // Get Association interface for object path
1185 for (const auto& [interface, values] :
1186 objDictEntry.second)
1187 {
1188 if (interface == "xyz.openbmc_project.Association")
1189 {
1190 for (const auto& [valueName, value] : values)
1191 {
1192 if (valueName == "endpoints")
1193 {
1194 const std::vector<std::string>*
1195 endpoints = std::get_if<
1196 std::vector<std::string>>(
1197 &value);
1198 if ((endpoints != nullptr) &&
1199 !endpoints->empty())
1200 {
1201 // Add inventory item to vector
1202 const std::string& invItemPath =
1203 endpoints->front();
1204 addInventoryItem(inventoryItems,
1205 invItemPath,
1206 sensorName);
1207 }
1208 }
1209 }
1210 }
1211 }
1212 break;
1213 }
1214 }
1215 }
1216
1217 // Now loop through the returned object paths again, this time to
1218 // find the leds associated with the inventory items we just found
1219 std::string inventoryAssocPath;
1220 inventoryAssocPath.reserve(128); // avoid memory allocations
1221 for (const auto& objDictEntry : resp)
1222 {
1223 const std::string& objPath =
1224 static_cast<const std::string&>(objDictEntry.first);
1225
1226 for (InventoryItem& inventoryItem : *inventoryItems)
1227 {
1228 inventoryAssocPath = inventoryItem.objectPath;
1229 inventoryAssocPath += "/leds";
1230 if (objPath == inventoryAssocPath)
1231 {
1232 for (const auto& [interface, values] :
1233 objDictEntry.second)
1234 {
1235 if (interface == "xyz.openbmc_project.Association")
1236 {
1237 for (const auto& [valueName, value] : values)
1238 {
1239 if (valueName == "endpoints")
1240 {
1241 const std::vector<std::string>*
1242 endpoints = std::get_if<
1243 std::vector<std::string>>(
1244 &value);
1245 if ((endpoints != nullptr) &&
1246 !endpoints->empty())
1247 {
1248 // Add inventory item to vector
1249 // Store LED path in inventory item
1250 const std::string& ledPath =
1251 endpoints->front();
1252 inventoryItem.ledObjectPath =
1253 ledPath;
1254 }
1255 }
1256 }
1257 }
1258 }
1259
1260 break;
1261 }
1262 }
1263 }
1264 callback(inventoryItems);
1265 BMCWEB_LOG_DEBUG("getInventoryItemAssociations respHandler exit");
1266 });
1267
1268 BMCWEB_LOG_DEBUG("getInventoryItemAssociations exit");
1269 }
1270
1271 /**
1272 * @brief Gets D-Bus data for inventory item leds associated with sensors.
1273 *
1274 * Uses the specified connections (services) to obtain D-Bus data for inventory
1275 * item leds associated with sensors. Stores the resulting data in the
1276 * inventoryItems vector.
1277 *
1278 * This data is later used to provide sensor property values in the JSON
1279 * response.
1280 *
1281 * Finds the inventory item led data asynchronously. Invokes callback when data
1282 * has been obtained.
1283 *
1284 * The callback must have the following signature:
1285 * @code
1286 * callback()
1287 * @endcode
1288 *
1289 * This function is called recursively, obtaining data asynchronously from one
1290 * connection in each call. This ensures the callback is not invoked until the
1291 * last asynchronous function has completed.
1292 *
1293 * @param sensorsAsyncResp Pointer to object holding response data.
1294 * @param inventoryItems D-Bus inventory items associated with sensors.
1295 * @param ledConnections Connections that provide data for the inventory leds.
1296 * @param callback Callback to invoke when inventory data has been obtained.
1297 * @param ledConnectionsIndex Current index in ledConnections. Only specified
1298 * in recursive calls to this function.
1299 */
1300 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)1301 void getInventoryLedData(
1302 std::shared_ptr<SensorsAsyncResp> sensorsAsyncResp,
1303 std::shared_ptr<std::vector<InventoryItem>> inventoryItems,
1304 std::shared_ptr<std::map<std::string, std::string>> ledConnections,
1305 Callback&& callback, size_t ledConnectionsIndex = 0)
1306 {
1307 BMCWEB_LOG_DEBUG("getInventoryLedData enter");
1308
1309 // If no more connections left, call callback
1310 if (ledConnectionsIndex >= ledConnections->size())
1311 {
1312 callback();
1313 BMCWEB_LOG_DEBUG("getInventoryLedData exit");
1314 return;
1315 }
1316
1317 // Get inventory item data from current connection
1318 auto it = ledConnections->begin();
1319 std::advance(it, ledConnectionsIndex);
1320 if (it != ledConnections->end())
1321 {
1322 const std::string& ledPath = (*it).first;
1323 const std::string& ledConnection = (*it).second;
1324 // Response handler for Get State property
1325 auto respHandler =
1326 [sensorsAsyncResp, inventoryItems, ledConnections, ledPath,
1327 callback = std::forward<Callback>(callback),
1328 ledConnectionsIndex](const boost::system::error_code& ec,
1329 const std::string& state) mutable {
1330 BMCWEB_LOG_DEBUG("getInventoryLedData respHandler enter");
1331 if (ec)
1332 {
1333 BMCWEB_LOG_ERROR(
1334 "getInventoryLedData respHandler DBus error {}", ec);
1335 messages::internalError(sensorsAsyncResp->asyncResp->res);
1336 return;
1337 }
1338
1339 BMCWEB_LOG_DEBUG("Led state: {}", state);
1340 // Find inventory item with this LED object path
1341 InventoryItem* inventoryItem =
1342 findInventoryItemForLed(*inventoryItems, ledPath);
1343 if (inventoryItem != nullptr)
1344 {
1345 // Store LED state in InventoryItem
1346 if (state.ends_with("On"))
1347 {
1348 inventoryItem->ledState = sensor_utils::LedState::ON;
1349 }
1350 else if (state.ends_with("Blink"))
1351 {
1352 inventoryItem->ledState = sensor_utils::LedState::BLINK;
1353 }
1354 else if (state.ends_with("Off"))
1355 {
1356 inventoryItem->ledState = sensor_utils::LedState::OFF;
1357 }
1358 else
1359 {
1360 inventoryItem->ledState =
1361 sensor_utils::LedState::UNKNOWN;
1362 }
1363 }
1364
1365 // Recurse to get LED data from next connection
1366 getInventoryLedData(sensorsAsyncResp, inventoryItems,
1367 ledConnections, std::move(callback),
1368 ledConnectionsIndex + 1);
1369
1370 BMCWEB_LOG_DEBUG("getInventoryLedData respHandler exit");
1371 };
1372
1373 // Get the State property for the current LED
1374 sdbusplus::asio::getProperty<std::string>(
1375 *crow::connections::systemBus, ledConnection, ledPath,
1376 "xyz.openbmc_project.Led.Physical", "State",
1377 std::move(respHandler));
1378 }
1379
1380 BMCWEB_LOG_DEBUG("getInventoryLedData exit");
1381 }
1382
1383 /**
1384 * @brief Gets LED data for LEDs associated with given inventory items.
1385 *
1386 * Gets the D-Bus connections (services) that provide LED data for the LEDs
1387 * associated with the specified inventory items. Then gets the LED data from
1388 * each connection and stores it in the inventory item.
1389 *
1390 * This data is later used to provide sensor property values in the JSON
1391 * response.
1392 *
1393 * Finds the LED data asynchronously. Invokes callback when information has
1394 * been obtained.
1395 *
1396 * The callback must have the following signature:
1397 * @code
1398 * callback()
1399 * @endcode
1400 *
1401 * @param sensorsAsyncResp Pointer to object holding response data.
1402 * @param inventoryItems D-Bus inventory items associated with sensors.
1403 * @param callback Callback to invoke when inventory items have been obtained.
1404 */
1405 template <typename Callback>
getInventoryLeds(std::shared_ptr<SensorsAsyncResp> sensorsAsyncResp,std::shared_ptr<std::vector<InventoryItem>> inventoryItems,Callback && callback)1406 void getInventoryLeds(
1407 std::shared_ptr<SensorsAsyncResp> sensorsAsyncResp,
1408 std::shared_ptr<std::vector<InventoryItem>> inventoryItems,
1409 Callback&& callback)
1410 {
1411 BMCWEB_LOG_DEBUG("getInventoryLeds enter");
1412
1413 const std::string path = "/xyz/openbmc_project";
1414 constexpr std::array<std::string_view, 1> interfaces = {
1415 "xyz.openbmc_project.Led.Physical"};
1416
1417 // Make call to ObjectMapper to find all inventory items
1418 dbus::utility::getSubTree(
1419 path, 0, interfaces,
1420 [callback = std::forward<Callback>(callback), sensorsAsyncResp,
1421 inventoryItems](
1422 const boost::system::error_code& ec,
1423 const dbus::utility::MapperGetSubTreeResponse& subtree) mutable {
1424 // Response handler for parsing output from GetSubTree
1425 BMCWEB_LOG_DEBUG("getInventoryLeds respHandler enter");
1426 if (ec)
1427 {
1428 messages::internalError(sensorsAsyncResp->asyncResp->res);
1429 BMCWEB_LOG_ERROR("getInventoryLeds respHandler DBus error {}",
1430 ec);
1431 return;
1432 }
1433
1434 // Build map of LED object paths to connections
1435 std::shared_ptr<std::map<std::string, std::string>> ledConnections =
1436 std::make_shared<std::map<std::string, std::string>>();
1437
1438 // Loop through objects from GetSubTree
1439 for (const std::pair<std::string,
1440 std::vector<std::pair<
1441 std::string, std::vector<std::string>>>>&
1442 object : subtree)
1443 {
1444 // Check if object path is LED for one of the specified
1445 // inventory items
1446 const std::string& ledPath = object.first;
1447 if (findInventoryItemForLed(*inventoryItems, ledPath) !=
1448 nullptr)
1449 {
1450 // Add mapping from ledPath to connection
1451 const std::string& connection =
1452 object.second.begin()->first;
1453 (*ledConnections)[ledPath] = connection;
1454 BMCWEB_LOG_DEBUG("Added mapping {} -> {}", ledPath,
1455 connection);
1456 }
1457 }
1458
1459 getInventoryLedData(sensorsAsyncResp, inventoryItems,
1460 ledConnections, std::move(callback));
1461 BMCWEB_LOG_DEBUG("getInventoryLeds respHandler exit");
1462 });
1463 BMCWEB_LOG_DEBUG("getInventoryLeds exit");
1464 }
1465
1466 /**
1467 * @brief Gets D-Bus data for Power Supply Attributes such as EfficiencyPercent
1468 *
1469 * Uses the specified connections (services) (currently assumes just one) to
1470 * obtain D-Bus data for Power Supply Attributes. Stores the resulting data in
1471 * the inventoryItems vector. Only stores data in Power Supply inventoryItems.
1472 *
1473 * This data is later used to provide sensor property values in the JSON
1474 * response.
1475 *
1476 * Finds the Power Supply Attributes data asynchronously. Invokes callback
1477 * when data has been obtained.
1478 *
1479 * The callback must have the following signature:
1480 * @code
1481 * callback(std::shared_ptr<std::vector<InventoryItem>> inventoryItems)
1482 * @endcode
1483 *
1484 * @param sensorsAsyncResp Pointer to object holding response data.
1485 * @param inventoryItems D-Bus inventory items associated with sensors.
1486 * @param psAttributesConnections Connections that provide data for the Power
1487 * Supply Attributes
1488 * @param callback Callback to invoke when data has been obtained.
1489 */
1490 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)1491 void getPowerSupplyAttributesData(
1492 const std::shared_ptr<SensorsAsyncResp>& sensorsAsyncResp,
1493 std::shared_ptr<std::vector<InventoryItem>> inventoryItems,
1494 const std::map<std::string, std::string>& psAttributesConnections,
1495 Callback&& callback)
1496 {
1497 BMCWEB_LOG_DEBUG("getPowerSupplyAttributesData enter");
1498
1499 if (psAttributesConnections.empty())
1500 {
1501 BMCWEB_LOG_DEBUG("Can't find PowerSupplyAttributes, no connections!");
1502 callback(inventoryItems);
1503 return;
1504 }
1505
1506 // Assuming just one connection (service) for now
1507 auto it = psAttributesConnections.begin();
1508
1509 const std::string& psAttributesPath = (*it).first;
1510 const std::string& psAttributesConnection = (*it).second;
1511
1512 // Response handler for Get DeratingFactor property
1513 auto respHandler = [sensorsAsyncResp, inventoryItems,
1514 callback = std::forward<Callback>(callback)](
1515 const boost::system::error_code& ec,
1516 uint32_t value) mutable {
1517 BMCWEB_LOG_DEBUG("getPowerSupplyAttributesData respHandler enter");
1518 if (ec)
1519 {
1520 BMCWEB_LOG_ERROR(
1521 "getPowerSupplyAttributesData respHandler DBus error {}", ec);
1522 messages::internalError(sensorsAsyncResp->asyncResp->res);
1523 return;
1524 }
1525
1526 BMCWEB_LOG_DEBUG("PS EfficiencyPercent value: {}", value);
1527 // Store value in Power Supply Inventory Items
1528 for (InventoryItem& inventoryItem : *inventoryItems)
1529 {
1530 if (inventoryItem.isPowerSupply)
1531 {
1532 inventoryItem.powerSupplyEfficiencyPercent =
1533 static_cast<int>(value);
1534 }
1535 }
1536
1537 BMCWEB_LOG_DEBUG("getPowerSupplyAttributesData respHandler exit");
1538 callback(inventoryItems);
1539 };
1540
1541 // Get the DeratingFactor property for the PowerSupplyAttributes
1542 // Currently only property on the interface/only one we care about
1543 sdbusplus::asio::getProperty<uint32_t>(
1544 *crow::connections::systemBus, psAttributesConnection, psAttributesPath,
1545 "xyz.openbmc_project.Control.PowerSupplyAttributes", "DeratingFactor",
1546 std::move(respHandler));
1547
1548 BMCWEB_LOG_DEBUG("getPowerSupplyAttributesData exit");
1549 }
1550
1551 /**
1552 * @brief Gets the Power Supply Attributes such as EfficiencyPercent
1553 *
1554 * Gets the D-Bus connection (service) that provides Power Supply Attributes
1555 * data. Then gets the Power Supply Attributes data from the connection
1556 * (currently just assumes 1 connection) and stores the data in the inventory
1557 * item.
1558 *
1559 * This data is later used to provide sensor property values in the JSON
1560 * response. DeratingFactor on D-Bus is mapped to EfficiencyPercent on Redfish.
1561 *
1562 * Finds the Power Supply Attributes data asynchronously. Invokes callback
1563 * when information has been obtained.
1564 *
1565 * The callback must have the following signature:
1566 * @code
1567 * callback(std::shared_ptr<std::vector<InventoryItem>> inventoryItems)
1568 * @endcode
1569 *
1570 * @param sensorsAsyncResp Pointer to object holding response data.
1571 * @param inventoryItems D-Bus inventory items associated with sensors.
1572 * @param callback Callback to invoke when data has been obtained.
1573 */
1574 template <typename Callback>
getPowerSupplyAttributes(std::shared_ptr<SensorsAsyncResp> sensorsAsyncResp,std::shared_ptr<std::vector<InventoryItem>> inventoryItems,Callback && callback)1575 void getPowerSupplyAttributes(
1576 std::shared_ptr<SensorsAsyncResp> sensorsAsyncResp,
1577 std::shared_ptr<std::vector<InventoryItem>> inventoryItems,
1578 Callback&& callback)
1579 {
1580 BMCWEB_LOG_DEBUG("getPowerSupplyAttributes enter");
1581
1582 // Only need the power supply attributes when the Power Schema
1583 if (sensorsAsyncResp->chassisSubNode != sensors::powerNodeStr)
1584 {
1585 BMCWEB_LOG_DEBUG("getPowerSupplyAttributes exit since not Power");
1586 callback(inventoryItems);
1587 return;
1588 }
1589
1590 constexpr std::array<std::string_view, 1> interfaces = {
1591 "xyz.openbmc_project.Control.PowerSupplyAttributes"};
1592
1593 // Make call to ObjectMapper to find the PowerSupplyAttributes service
1594 dbus::utility::getSubTree(
1595 "/xyz/openbmc_project", 0, interfaces,
1596 [callback = std::forward<Callback>(callback), sensorsAsyncResp,
1597 inventoryItems](
1598 const boost::system::error_code& ec,
1599 const dbus::utility::MapperGetSubTreeResponse& subtree) mutable {
1600 // Response handler for parsing output from GetSubTree
1601 BMCWEB_LOG_DEBUG("getPowerSupplyAttributes respHandler enter");
1602 if (ec)
1603 {
1604 messages::internalError(sensorsAsyncResp->asyncResp->res);
1605 BMCWEB_LOG_ERROR(
1606 "getPowerSupplyAttributes respHandler DBus error {}", ec);
1607 return;
1608 }
1609 if (subtree.empty())
1610 {
1611 BMCWEB_LOG_DEBUG("Can't find Power Supply Attributes!");
1612 callback(inventoryItems);
1613 return;
1614 }
1615
1616 // Currently we only support 1 power supply attribute, use this for
1617 // all the power supplies. Build map of object path to connection.
1618 // Assume just 1 connection and 1 path for now.
1619 std::map<std::string, std::string> psAttributesConnections;
1620
1621 if (subtree[0].first.empty() || subtree[0].second.empty())
1622 {
1623 BMCWEB_LOG_DEBUG("Power Supply Attributes mapper error!");
1624 callback(inventoryItems);
1625 return;
1626 }
1627
1628 const std::string& psAttributesPath = subtree[0].first;
1629 const std::string& connection = subtree[0].second.begin()->first;
1630
1631 if (connection.empty())
1632 {
1633 BMCWEB_LOG_DEBUG("Power Supply Attributes mapper error!");
1634 callback(inventoryItems);
1635 return;
1636 }
1637
1638 psAttributesConnections[psAttributesPath] = connection;
1639 BMCWEB_LOG_DEBUG("Added mapping {} -> {}", psAttributesPath,
1640 connection);
1641
1642 getPowerSupplyAttributesData(sensorsAsyncResp, inventoryItems,
1643 psAttributesConnections,
1644 std::move(callback));
1645 BMCWEB_LOG_DEBUG("getPowerSupplyAttributes respHandler exit");
1646 });
1647 BMCWEB_LOG_DEBUG("getPowerSupplyAttributes exit");
1648 }
1649
1650 /**
1651 * @brief Gets inventory items associated with sensors.
1652 *
1653 * Finds the inventory items that are associated with the specified sensors.
1654 * Then gets D-Bus data for the inventory items, such as presence and VPD.
1655 *
1656 * This data is later used to provide sensor property values in the JSON
1657 * response.
1658 *
1659 * Finds the inventory items asynchronously. Invokes callback when the
1660 * inventory items have been obtained.
1661 *
1662 * The callback must have the following signature:
1663 * @code
1664 * callback(std::shared_ptr<std::vector<InventoryItem>> inventoryItems)
1665 * @endcode
1666 *
1667 * @param sensorsAsyncResp Pointer to object holding response data.
1668 * @param sensorNames All sensors within the current chassis.
1669 * implements ObjectManager.
1670 * @param callback Callback to invoke when inventory items have been obtained.
1671 */
1672 template <typename Callback>
1673 inline void
getInventoryItems(std::shared_ptr<SensorsAsyncResp> sensorsAsyncResp,const std::shared_ptr<std::set<std::string>> sensorNames,Callback && callback)1674 getInventoryItems(std::shared_ptr<SensorsAsyncResp> sensorsAsyncResp,
1675 const std::shared_ptr<std::set<std::string>> sensorNames,
1676 Callback&& callback)
1677 {
1678 BMCWEB_LOG_DEBUG("getInventoryItems enter");
1679 auto getInventoryItemAssociationsCb =
1680 [sensorsAsyncResp, callback = std::forward<Callback>(callback)](
1681 std::shared_ptr<std::vector<InventoryItem>>
1682 inventoryItems) mutable {
1683 BMCWEB_LOG_DEBUG("getInventoryItemAssociationsCb enter");
1684 auto getInventoryItemsConnectionsCb =
1685 [sensorsAsyncResp, inventoryItems,
1686 callback = std::forward<Callback>(callback)](
1687 std::shared_ptr<std::set<std::string>>
1688 invConnections) mutable {
1689 BMCWEB_LOG_DEBUG("getInventoryItemsConnectionsCb enter");
1690 auto getInventoryItemsDataCb =
1691 [sensorsAsyncResp, inventoryItems,
1692 callback =
1693 std::forward<Callback>(callback)]() mutable {
1694 BMCWEB_LOG_DEBUG("getInventoryItemsDataCb enter");
1695
1696 auto getInventoryLedsCb =
1697 [sensorsAsyncResp, inventoryItems,
1698 callback = std::forward<Callback>(
1699 callback)]() mutable {
1700 BMCWEB_LOG_DEBUG(
1701 "getInventoryLedsCb enter");
1702 // Find Power Supply Attributes and get the
1703 // data
1704 getPowerSupplyAttributes(
1705 sensorsAsyncResp, inventoryItems,
1706 std::move(callback));
1707 BMCWEB_LOG_DEBUG("getInventoryLedsCb exit");
1708 };
1709
1710 // Find led connections and get the data
1711 getInventoryLeds(sensorsAsyncResp, inventoryItems,
1712 std::move(getInventoryLedsCb));
1713 BMCWEB_LOG_DEBUG("getInventoryItemsDataCb exit");
1714 };
1715
1716 // Get inventory item data from connections
1717 getInventoryItemsData(sensorsAsyncResp, inventoryItems,
1718 invConnections,
1719 std::move(getInventoryItemsDataCb));
1720 BMCWEB_LOG_DEBUG("getInventoryItemsConnectionsCb exit");
1721 };
1722
1723 // Get connections that provide inventory item data
1724 getInventoryItemsConnections(
1725 sensorsAsyncResp, inventoryItems,
1726 std::move(getInventoryItemsConnectionsCb));
1727 BMCWEB_LOG_DEBUG("getInventoryItemAssociationsCb exit");
1728 };
1729
1730 // Get associations from sensors to inventory items
1731 getInventoryItemAssociations(sensorsAsyncResp, sensorNames,
1732 std::move(getInventoryItemAssociationsCb));
1733 BMCWEB_LOG_DEBUG("getInventoryItems exit");
1734 }
1735
1736 /**
1737 * @brief Returns JSON PowerSupply object for the specified inventory item.
1738 *
1739 * Searches for a JSON PowerSupply object that matches the specified inventory
1740 * item. If one is not found, a new PowerSupply object is added to the JSON
1741 * array.
1742 *
1743 * Multiple sensors are often associated with one power supply inventory item.
1744 * As a result, multiple sensor values are stored in one JSON PowerSupply
1745 * object.
1746 *
1747 * @param powerSupplyArray JSON array containing Redfish PowerSupply objects.
1748 * @param inventoryItem Inventory item for the power supply.
1749 * @param chassisId Chassis that contains the power supply.
1750 * @return JSON PowerSupply object for the specified inventory item.
1751 */
getPowerSupply(nlohmann::json & powerSupplyArray,const InventoryItem & inventoryItem,const std::string & chassisId)1752 inline nlohmann::json& getPowerSupply(nlohmann::json& powerSupplyArray,
1753 const InventoryItem& inventoryItem,
1754 const std::string& chassisId)
1755 {
1756 std::string nameS;
1757 nameS.resize(inventoryItem.name.size());
1758 std::ranges::replace_copy(inventoryItem.name, nameS.begin(), '_', ' ');
1759 // Check if matching PowerSupply object already exists in JSON array
1760 for (nlohmann::json& powerSupply : powerSupplyArray)
1761 {
1762 nlohmann::json::iterator nameIt = powerSupply.find("Name");
1763 if (nameIt == powerSupply.end())
1764 {
1765 continue;
1766 }
1767 const std::string* name = nameIt->get_ptr<std::string*>();
1768 if (name == nullptr)
1769 {
1770 continue;
1771 }
1772 if (nameS == *name)
1773 {
1774 return powerSupply;
1775 }
1776 }
1777
1778 // Add new PowerSupply object to JSON array
1779 powerSupplyArray.push_back({});
1780 nlohmann::json& powerSupply = powerSupplyArray.back();
1781 boost::urls::url url =
1782 boost::urls::format("/redfish/v1/Chassis/{}/Power", chassisId);
1783 url.set_fragment(("/PowerSupplies"_json_pointer).to_string());
1784 powerSupply["@odata.id"] = std::move(url);
1785 std::string escaped;
1786 escaped.resize(inventoryItem.name.size());
1787 std::ranges::replace_copy(inventoryItem.name, escaped.begin(), '_', ' ');
1788 powerSupply["Name"] = std::move(escaped);
1789 powerSupply["Manufacturer"] = inventoryItem.manufacturer;
1790 powerSupply["Model"] = inventoryItem.model;
1791 powerSupply["PartNumber"] = inventoryItem.partNumber;
1792 powerSupply["SerialNumber"] = inventoryItem.serialNumber;
1793 sensor_utils::setLedState(powerSupply, &inventoryItem);
1794
1795 if (inventoryItem.powerSupplyEfficiencyPercent >= 0)
1796 {
1797 powerSupply["EfficiencyPercent"] =
1798 inventoryItem.powerSupplyEfficiencyPercent;
1799 }
1800
1801 powerSupply["Status"]["State"] =
1802 sensor_utils::getState(&inventoryItem, true);
1803 const char* health = inventoryItem.isFunctional ? "OK" : "Critical";
1804 powerSupply["Status"]["Health"] = health;
1805
1806 return powerSupply;
1807 }
1808
1809 /**
1810 * @brief Gets the values of the specified sensors.
1811 *
1812 * Stores the results as JSON in the SensorsAsyncResp.
1813 *
1814 * Gets the sensor values asynchronously. Stores the results later when the
1815 * information has been obtained.
1816 *
1817 * The sensorNames set contains all requested sensors for the current chassis.
1818 *
1819 * To minimize the number of DBus calls, the DBus method
1820 * org.freedesktop.DBus.ObjectManager.GetManagedObjects() is used to get the
1821 * values of all sensors provided by a connection (service).
1822 *
1823 * The connections set contains all the connections that provide sensor values.
1824 *
1825 * The InventoryItem vector contains D-Bus inventory items associated with the
1826 * sensors. Inventory item data is needed for some Redfish sensor properties.
1827 *
1828 * @param SensorsAsyncResp Pointer to object holding response data.
1829 * @param sensorNames All requested sensors within the current chassis.
1830 * @param connections Connections that provide sensor values.
1831 * implements ObjectManager.
1832 * @param inventoryItems Inventory items associated with the sensors.
1833 */
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)1834 inline void getSensorData(
1835 const std::shared_ptr<SensorsAsyncResp>& sensorsAsyncResp,
1836 const std::shared_ptr<std::set<std::string>>& sensorNames,
1837 const std::set<std::string>& connections,
1838 const std::shared_ptr<std::vector<InventoryItem>>& inventoryItems)
1839 {
1840 BMCWEB_LOG_DEBUG("getSensorData enter");
1841 // Get managed objects from all services exposing sensors
1842 for (const std::string& connection : connections)
1843 {
1844 sdbusplus::message::object_path sensorPath(
1845 "/xyz/openbmc_project/sensors");
1846 dbus::utility::getManagedObjects(
1847 connection, sensorPath,
1848 [sensorsAsyncResp, sensorNames,
1849 inventoryItems](const boost::system::error_code& ec,
1850 const dbus::utility::ManagedObjectType& resp) {
1851 BMCWEB_LOG_DEBUG("getManagedObjectsCb enter");
1852 if (ec)
1853 {
1854 BMCWEB_LOG_ERROR("getManagedObjectsCb DBUS error: {}", ec);
1855 messages::internalError(sensorsAsyncResp->asyncResp->res);
1856 return;
1857 }
1858 auto chassisSubNode = sensor_utils::chassisSubNodeFromString(
1859 sensorsAsyncResp->chassisSubNode);
1860 // Go through all objects and update response with sensor data
1861 for (const auto& objDictEntry : resp)
1862 {
1863 const std::string& objPath =
1864 static_cast<const std::string&>(objDictEntry.first);
1865 BMCWEB_LOG_DEBUG("getManagedObjectsCb parsing object {}",
1866 objPath);
1867
1868 std::vector<std::string> split;
1869 // Reserve space for
1870 // /xyz/openbmc_project/sensors/<name>/<subname>
1871 split.reserve(6);
1872 // NOLINTNEXTLINE
1873 bmcweb::split(split, objPath, '/');
1874 if (split.size() < 6)
1875 {
1876 BMCWEB_LOG_ERROR("Got path that isn't long enough {}",
1877 objPath);
1878 continue;
1879 }
1880 // These indexes aren't intuitive, as split puts an empty
1881 // string at the beginning
1882 const std::string& sensorType = split[4];
1883 const std::string& sensorName = split[5];
1884 BMCWEB_LOG_DEBUG("sensorName {} sensorType {}", sensorName,
1885 sensorType);
1886 if (sensorNames->find(objPath) == sensorNames->end())
1887 {
1888 BMCWEB_LOG_DEBUG("{} not in sensor list ", sensorName);
1889 continue;
1890 }
1891
1892 // Find inventory item (if any) associated with sensor
1893 InventoryItem* inventoryItem =
1894 findInventoryItemForSensor(inventoryItems, objPath);
1895
1896 const std::string& sensorSchema =
1897 sensorsAsyncResp->chassisSubNode;
1898
1899 nlohmann::json* sensorJson = nullptr;
1900
1901 if (sensorSchema == sensors::sensorsNodeStr &&
1902 !sensorsAsyncResp->efficientExpand)
1903 {
1904 std::string sensorId =
1905 redfish::sensor_utils::getSensorId(sensorName,
1906 sensorType);
1907
1908 sensorsAsyncResp->asyncResp->res
1909 .jsonValue["@odata.id"] = boost::urls::format(
1910 "/redfish/v1/Chassis/{}/{}/{}",
1911 sensorsAsyncResp->chassisId,
1912 sensorsAsyncResp->chassisSubNode, sensorId);
1913 sensorJson =
1914 &(sensorsAsyncResp->asyncResp->res.jsonValue);
1915 }
1916 else
1917 {
1918 std::string fieldName;
1919 if (sensorsAsyncResp->efficientExpand)
1920 {
1921 fieldName = "Members";
1922 }
1923 else if (sensorType == "temperature")
1924 {
1925 fieldName = "Temperatures";
1926 }
1927 else if (sensorType == "fan" ||
1928 sensorType == "fan_tach" ||
1929 sensorType == "fan_pwm")
1930 {
1931 fieldName = "Fans";
1932 }
1933 else if (sensorType == "voltage")
1934 {
1935 fieldName = "Voltages";
1936 }
1937 else if (sensorType == "power")
1938 {
1939 if (sensorName == "total_power")
1940 {
1941 fieldName = "PowerControl";
1942 }
1943 else if ((inventoryItem != nullptr) &&
1944 (inventoryItem->isPowerSupply))
1945 {
1946 fieldName = "PowerSupplies";
1947 }
1948 else
1949 {
1950 // Other power sensors are in SensorCollection
1951 continue;
1952 }
1953 }
1954 else
1955 {
1956 BMCWEB_LOG_ERROR(
1957 "Unsure how to handle sensorType {}",
1958 sensorType);
1959 continue;
1960 }
1961
1962 nlohmann::json& tempArray =
1963 sensorsAsyncResp->asyncResp->res
1964 .jsonValue[fieldName];
1965 if (fieldName == "PowerControl")
1966 {
1967 if (tempArray.empty())
1968 {
1969 // Put multiple "sensors" into a single
1970 // PowerControl. Follows MemberId naming and
1971 // naming in power.hpp.
1972 nlohmann::json::object_t power;
1973 boost::urls::url url = boost::urls::format(
1974 "/redfish/v1/Chassis/{}/{}",
1975 sensorsAsyncResp->chassisId,
1976 sensorsAsyncResp->chassisSubNode);
1977 url.set_fragment(
1978 (""_json_pointer / fieldName / "0")
1979 .to_string());
1980 power["@odata.id"] = std::move(url);
1981 tempArray.emplace_back(std::move(power));
1982 }
1983 sensorJson = &(tempArray.back());
1984 }
1985 else if (fieldName == "PowerSupplies")
1986 {
1987 if (inventoryItem != nullptr)
1988 {
1989 sensorJson = &(getPowerSupply(
1990 tempArray, *inventoryItem,
1991 sensorsAsyncResp->chassisId));
1992 }
1993 }
1994 else if (fieldName == "Members")
1995 {
1996 std::string sensorId =
1997 redfish::sensor_utils::getSensorId(sensorName,
1998 sensorType);
1999
2000 nlohmann::json::object_t member;
2001 member["@odata.id"] = boost::urls::format(
2002 "/redfish/v1/Chassis/{}/{}/{}",
2003 sensorsAsyncResp->chassisId,
2004 sensorsAsyncResp->chassisSubNode, sensorId);
2005 tempArray.emplace_back(std::move(member));
2006 sensorJson = &(tempArray.back());
2007 }
2008 else
2009 {
2010 nlohmann::json::object_t member;
2011 boost::urls::url url = boost::urls::format(
2012 "/redfish/v1/Chassis/{}/{}",
2013 sensorsAsyncResp->chassisId,
2014 sensorsAsyncResp->chassisSubNode);
2015 url.set_fragment(
2016 (""_json_pointer / fieldName).to_string());
2017 member["@odata.id"] = std::move(url);
2018 tempArray.emplace_back(std::move(member));
2019 sensorJson = &(tempArray.back());
2020 }
2021 }
2022
2023 if (sensorJson != nullptr)
2024 {
2025 objectInterfacesToJson(
2026 sensorName, sensorType, chassisSubNode,
2027 objDictEntry.second, *sensorJson, inventoryItem);
2028
2029 std::string path = "/xyz/openbmc_project/sensors/";
2030 path += sensorType;
2031 path += "/";
2032 path += sensorName;
2033 sensorsAsyncResp->addMetadata(*sensorJson, path);
2034 }
2035 }
2036 if (sensorsAsyncResp.use_count() == 1)
2037 {
2038 sortJSONResponse(sensorsAsyncResp);
2039 if (chassisSubNode ==
2040 sensor_utils::ChassisSubNode::sensorsNode &&
2041 sensorsAsyncResp->efficientExpand)
2042 {
2043 sensorsAsyncResp->asyncResp->res
2044 .jsonValue["Members@odata.count"] =
2045 sensorsAsyncResp->asyncResp->res
2046 .jsonValue["Members"]
2047 .size();
2048 }
2049 else if (chassisSubNode ==
2050 sensor_utils::ChassisSubNode::thermalNode)
2051 {
2052 populateFanRedundancy(sensorsAsyncResp);
2053 }
2054 }
2055 BMCWEB_LOG_DEBUG("getManagedObjectsCb exit");
2056 });
2057 }
2058 BMCWEB_LOG_DEBUG("getSensorData exit");
2059 }
2060
2061 inline void
processSensorList(const std::shared_ptr<SensorsAsyncResp> & sensorsAsyncResp,const std::shared_ptr<std::set<std::string>> & sensorNames)2062 processSensorList(const std::shared_ptr<SensorsAsyncResp>& sensorsAsyncResp,
2063 const std::shared_ptr<std::set<std::string>>& sensorNames)
2064 {
2065 auto getConnectionCb = [sensorsAsyncResp, sensorNames](
2066 const std::set<std::string>& connections) {
2067 BMCWEB_LOG_DEBUG("getConnectionCb enter");
2068 auto getInventoryItemsCb =
2069 [sensorsAsyncResp, sensorNames, connections](
2070 const std::shared_ptr<std::vector<InventoryItem>>&
2071 inventoryItems) mutable {
2072 BMCWEB_LOG_DEBUG("getInventoryItemsCb enter");
2073 // Get sensor data and store results in JSON
2074 getSensorData(sensorsAsyncResp, sensorNames, connections,
2075 inventoryItems);
2076 BMCWEB_LOG_DEBUG("getInventoryItemsCb exit");
2077 };
2078
2079 // Get inventory items associated with sensors
2080 getInventoryItems(sensorsAsyncResp, sensorNames,
2081 std::move(getInventoryItemsCb));
2082
2083 BMCWEB_LOG_DEBUG("getConnectionCb exit");
2084 };
2085
2086 // Get set of connections that provide sensor values
2087 getConnections(sensorsAsyncResp, sensorNames, std::move(getConnectionCb));
2088 }
2089
2090 /**
2091 * @brief Entry point for retrieving sensors data related to requested
2092 * chassis.
2093 * @param SensorsAsyncResp Pointer to object holding response data
2094 */
2095 inline void
getChassisData(const std::shared_ptr<SensorsAsyncResp> & sensorsAsyncResp)2096 getChassisData(const std::shared_ptr<SensorsAsyncResp>& sensorsAsyncResp)
2097 {
2098 BMCWEB_LOG_DEBUG("getChassisData enter");
2099 auto getChassisCb =
2100 [sensorsAsyncResp](
2101 const std::shared_ptr<std::set<std::string>>& sensorNames) {
2102 BMCWEB_LOG_DEBUG("getChassisCb enter");
2103 processSensorList(sensorsAsyncResp, sensorNames);
2104 BMCWEB_LOG_DEBUG("getChassisCb exit");
2105 };
2106 // SensorCollection doesn't contain the Redundancy property
2107 if (sensorsAsyncResp->chassisSubNode != sensors::sensorsNodeStr)
2108 {
2109 sensorsAsyncResp->asyncResp->res.jsonValue["Redundancy"] =
2110 nlohmann::json::array();
2111 }
2112 // Get set of sensors in chassis
2113 getChassis(sensorsAsyncResp->asyncResp, sensorsAsyncResp->chassisId,
2114 sensorsAsyncResp->chassisSubNode, sensorsAsyncResp->types,
2115 std::move(getChassisCb));
2116 BMCWEB_LOG_DEBUG("getChassisData exit");
2117 }
2118
2119 /**
2120 * @brief Find the requested sensorName in the list of all sensors supplied by
2121 * the chassis node
2122 *
2123 * @param sensorName The sensor name supplied in the PATCH request
2124 * @param sensorsList The list of sensors managed by the chassis node
2125 * @param sensorsModified The list of sensors that were found as a result of
2126 * repeated calls to this function
2127 */
findSensorNameUsingSensorPath(std::string_view sensorName,const std::set<std::string> & sensorsList,std::set<std::string> & sensorsModified)2128 inline bool findSensorNameUsingSensorPath(
2129 std::string_view sensorName, const std::set<std::string>& sensorsList,
2130 std::set<std::string>& sensorsModified)
2131 {
2132 for (const auto& chassisSensor : sensorsList)
2133 {
2134 sdbusplus::message::object_path path(chassisSensor);
2135 std::string thisSensorName = path.filename();
2136 if (thisSensorName.empty())
2137 {
2138 continue;
2139 }
2140 if (thisSensorName == sensorName)
2141 {
2142 sensorsModified.emplace(chassisSensor);
2143 return true;
2144 }
2145 }
2146 return false;
2147 }
2148
2149 /**
2150 * @brief Entry point for overriding sensor values of given sensor
2151 *
2152 * @param sensorAsyncResp response object
2153 * @param allCollections Collections extract from sensors' request patch info
2154 * @param chassisSubNode Chassis Node for which the query has to happen
2155 */
setSensorsOverride(const std::shared_ptr<SensorsAsyncResp> & sensorAsyncResp,std::unordered_map<std::string,std::vector<nlohmann::json::object_t>> & allCollections)2156 inline void setSensorsOverride(
2157 const std::shared_ptr<SensorsAsyncResp>& sensorAsyncResp,
2158 std::unordered_map<std::string, std::vector<nlohmann::json::object_t>>&
2159 allCollections)
2160 {
2161 BMCWEB_LOG_INFO("setSensorsOverride for subNode{}",
2162 sensorAsyncResp->chassisSubNode);
2163
2164 std::string_view propertyValueName;
2165 std::unordered_map<std::string, std::pair<double, std::string>> overrideMap;
2166 std::string memberId;
2167 double value = 0.0;
2168 for (auto& collectionItems : allCollections)
2169 {
2170 if (collectionItems.first == "Temperatures")
2171 {
2172 propertyValueName = "ReadingCelsius";
2173 }
2174 else if (collectionItems.first == "Fans")
2175 {
2176 propertyValueName = "Reading";
2177 }
2178 else
2179 {
2180 propertyValueName = "ReadingVolts";
2181 }
2182 for (auto& item : collectionItems.second)
2183 {
2184 if (!json_util::readJsonObject(
2185 item, sensorAsyncResp->asyncResp->res, "MemberId", memberId,
2186 propertyValueName, value))
2187 {
2188 return;
2189 }
2190 overrideMap.emplace(memberId,
2191 std::make_pair(value, collectionItems.first));
2192 }
2193 }
2194
2195 auto getChassisSensorListCb = [sensorAsyncResp, overrideMap,
2196 propertyValueNameStr =
2197 std::string(propertyValueName)](
2198 const std::shared_ptr<
2199 std::set<std::string>>& sensorsList) {
2200 // Match sensor names in the PATCH request to those managed by the
2201 // chassis node
2202 const std::shared_ptr<std::set<std::string>> sensorNames =
2203 std::make_shared<std::set<std::string>>();
2204 for (const auto& item : overrideMap)
2205 {
2206 const auto& sensor = item.first;
2207 std::pair<std::string, std::string> sensorNameType =
2208 redfish::sensor_utils::splitSensorNameAndType(sensor);
2209 if (!findSensorNameUsingSensorPath(sensorNameType.second,
2210 *sensorsList, *sensorNames))
2211 {
2212 BMCWEB_LOG_INFO("Unable to find memberId {}", item.first);
2213 messages::resourceNotFound(sensorAsyncResp->asyncResp->res,
2214 item.second.second, item.first);
2215 return;
2216 }
2217 }
2218 // Get the connection to which the memberId belongs
2219 auto getObjectsWithConnectionCb = [sensorAsyncResp, overrideMap,
2220 propertyValueNameStr](
2221 const std::set<
2222 std::string>& /*connections*/,
2223 const std::set<std::pair<
2224 std::string, std::string>>&
2225 objectsWithConnection) {
2226 if (objectsWithConnection.size() != overrideMap.size())
2227 {
2228 BMCWEB_LOG_INFO(
2229 "Unable to find all objects with proper connection {} requested {}",
2230 objectsWithConnection.size(), overrideMap.size());
2231 messages::resourceNotFound(
2232 sensorAsyncResp->asyncResp->res,
2233 sensorAsyncResp->chassisSubNode == sensors::thermalNodeStr
2234 ? "Temperatures"
2235 : "Voltages",
2236 "Count");
2237 return;
2238 }
2239 for (const auto& item : objectsWithConnection)
2240 {
2241 sdbusplus::message::object_path path(item.first);
2242 std::string sensorName = path.filename();
2243 if (sensorName.empty())
2244 {
2245 messages::internalError(sensorAsyncResp->asyncResp->res);
2246 return;
2247 }
2248 std::string id = redfish::sensor_utils::getSensorId(
2249 sensorName, path.parent_path().filename());
2250
2251 const auto& iterator = overrideMap.find(id);
2252 if (iterator == overrideMap.end())
2253 {
2254 BMCWEB_LOG_INFO("Unable to find sensor object{}",
2255 item.first);
2256 messages::internalError(sensorAsyncResp->asyncResp->res);
2257 return;
2258 }
2259 setDbusProperty(sensorAsyncResp->asyncResp,
2260 propertyValueNameStr, item.second, item.first,
2261 "xyz.openbmc_project.Sensor.Value", "Value",
2262 iterator->second.first);
2263 }
2264 };
2265 // Get object with connection for the given sensor name
2266 getObjectsWithConnection(sensorAsyncResp, sensorNames,
2267 std::move(getObjectsWithConnectionCb));
2268 };
2269 // get full sensor list for the given chassisId and cross verify the sensor.
2270 getChassis(sensorAsyncResp->asyncResp, sensorAsyncResp->chassisId,
2271 sensorAsyncResp->chassisSubNode, sensorAsyncResp->types,
2272 std::move(getChassisSensorListCb));
2273 }
2274
2275 /**
2276 * @brief Retrieves mapping of Redfish URIs to sensor value property to D-Bus
2277 * path of the sensor.
2278 *
2279 * Function builds valid Redfish response for sensor query of given chassis and
2280 * node. It then builds metadata about Redfish<->D-Bus correlations and provides
2281 * it to caller in a callback.
2282 *
2283 * @param chassis Chassis for which retrieval should be performed
2284 * @param node Node (group) of sensors. See sensor_utils::node for supported
2285 * values
2286 * @param mapComplete Callback to be called with retrieval result
2287 */
2288 template <typename Callback>
retrieveUriToDbusMap(const std::string & chassis,const std::string & node,Callback && mapComplete)2289 inline void retrieveUriToDbusMap(
2290 const std::string& chassis, const std::string& node, Callback&& mapComplete)
2291 {
2292 decltype(sensors::paths)::const_iterator pathIt =
2293 std::find_if(sensors::paths.cbegin(), sensors::paths.cend(),
2294 [&node](auto&& val) { return val.first == node; });
2295 if (pathIt == sensors::paths.cend())
2296 {
2297 BMCWEB_LOG_ERROR("Wrong node provided : {}", node);
2298 std::map<std::string, std::string> noop;
2299 mapComplete(boost::beast::http::status::bad_request, noop);
2300 return;
2301 }
2302
2303 auto asyncResp = std::make_shared<bmcweb::AsyncResp>();
2304 auto callback =
2305 [asyncResp, mapCompleteCb = std::forward<Callback>(mapComplete)](
2306 const boost::beast::http::status status,
2307 const std::map<std::string, std::string>& uriToDbus) {
2308 mapCompleteCb(status, uriToDbus);
2309 };
2310
2311 auto resp = std::make_shared<SensorsAsyncResp>(
2312 asyncResp, chassis, pathIt->second, node, std::move(callback));
2313 getChassisData(resp);
2314 }
2315
2316 namespace sensors
2317 {
2318
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)2319 inline void getChassisCallback(
2320 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2321 std::string_view chassisId, std::string_view chassisSubNode,
2322 const std::shared_ptr<std::set<std::string>>& sensorNames)
2323 {
2324 BMCWEB_LOG_DEBUG("getChassisCallback enter ");
2325
2326 nlohmann::json& entriesArray = asyncResp->res.jsonValue["Members"];
2327 for (const std::string& sensor : *sensorNames)
2328 {
2329 BMCWEB_LOG_DEBUG("Adding sensor: {}", sensor);
2330
2331 sdbusplus::message::object_path path(sensor);
2332 std::string sensorName = path.filename();
2333 if (sensorName.empty())
2334 {
2335 BMCWEB_LOG_ERROR("Invalid sensor path: {}", sensor);
2336 messages::internalError(asyncResp->res);
2337 return;
2338 }
2339 std::string type = path.parent_path().filename();
2340 std::string id = redfish::sensor_utils::getSensorId(sensorName, type);
2341
2342 nlohmann::json::object_t member;
2343 member["@odata.id"] = boost::urls::format(
2344 "/redfish/v1/Chassis/{}/{}/{}", chassisId, chassisSubNode, id);
2345
2346 entriesArray.emplace_back(std::move(member));
2347 }
2348
2349 asyncResp->res.jsonValue["Members@odata.count"] = entriesArray.size();
2350 BMCWEB_LOG_DEBUG("getChassisCallback exit");
2351 }
2352
handleSensorCollectionGet(App & app,const crow::Request & req,const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & chassisId)2353 inline void handleSensorCollectionGet(
2354 App& app, const crow::Request& req,
2355 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2356 const std::string& chassisId)
2357 {
2358 query_param::QueryCapabilities capabilities = {
2359 .canDelegateExpandLevel = 1,
2360 };
2361 query_param::Query delegatedQuery;
2362 if (!redfish::setUpRedfishRouteWithDelegation(app, req, asyncResp,
2363 delegatedQuery, capabilities))
2364 {
2365 return;
2366 }
2367
2368 if (delegatedQuery.expandType != query_param::ExpandType::None)
2369 {
2370 // we perform efficient expand.
2371 auto sensorsAsyncResp = std::make_shared<SensorsAsyncResp>(
2372 asyncResp, chassisId, sensors::dbus::sensorPaths,
2373 sensors::sensorsNodeStr,
2374 /*efficientExpand=*/true);
2375 getChassisData(sensorsAsyncResp);
2376
2377 BMCWEB_LOG_DEBUG(
2378 "SensorCollection doGet exit via efficient expand handler");
2379 return;
2380 }
2381
2382 // We get all sensors as hyperlinkes in the chassis (this
2383 // implies we reply on the default query parameters handler)
2384 getChassis(asyncResp, chassisId, sensors::sensorsNodeStr, dbus::sensorPaths,
2385 std::bind_front(sensors::getChassisCallback, asyncResp,
2386 chassisId, sensors::sensorsNodeStr));
2387 }
2388
2389 inline void
getSensorFromDbus(const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & sensorPath,const::dbus::utility::MapperGetObject & mapperResponse)2390 getSensorFromDbus(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2391 const std::string& sensorPath,
2392 const ::dbus::utility::MapperGetObject& mapperResponse)
2393 {
2394 if (mapperResponse.size() != 1)
2395 {
2396 messages::internalError(asyncResp->res);
2397 return;
2398 }
2399 const auto& valueIface = *mapperResponse.begin();
2400 const std::string& connectionName = valueIface.first;
2401 BMCWEB_LOG_DEBUG("Looking up {}", connectionName);
2402 BMCWEB_LOG_DEBUG("Path {}", sensorPath);
2403
2404 sdbusplus::asio::getAllProperties(
2405 *crow::connections::systemBus, connectionName, sensorPath, "",
2406 [asyncResp,
2407 sensorPath](const boost::system::error_code& ec,
2408 const ::dbus::utility::DBusPropertiesMap& valuesDict) {
2409 if (ec)
2410 {
2411 messages::internalError(asyncResp->res);
2412 return;
2413 }
2414 sdbusplus::message::object_path path(sensorPath);
2415 std::string name = path.filename();
2416 path = path.parent_path();
2417 std::string type = path.filename();
2418 sensor_utils::objectPropertiesToJson(
2419 name, type, sensor_utils::ChassisSubNode::sensorsNode,
2420 valuesDict, asyncResp->res.jsonValue, nullptr);
2421 });
2422 }
2423
handleSensorGet(App & app,const crow::Request & req,const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & chassisId,const std::string & sensorId)2424 inline void handleSensorGet(App& app, const crow::Request& req,
2425 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2426 const std::string& chassisId,
2427 const std::string& sensorId)
2428 {
2429 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
2430 {
2431 return;
2432 }
2433 std::pair<std::string, std::string> nameType =
2434 redfish::sensor_utils::splitSensorNameAndType(sensorId);
2435 if (nameType.first.empty() || nameType.second.empty())
2436 {
2437 messages::resourceNotFound(asyncResp->res, sensorId, "Sensor");
2438 return;
2439 }
2440
2441 asyncResp->res.jsonValue["@odata.id"] = boost::urls::format(
2442 "/redfish/v1/Chassis/{}/Sensors/{}", chassisId, sensorId);
2443
2444 BMCWEB_LOG_DEBUG("Sensor doGet enter");
2445
2446 constexpr std::array<std::string_view, 1> interfaces = {
2447 "xyz.openbmc_project.Sensor.Value"};
2448 std::string sensorPath = "/xyz/openbmc_project/sensors/" + nameType.first +
2449 '/' + nameType.second;
2450 // Get a list of all of the sensors that implement Sensor.Value
2451 // and get the path and service name associated with the sensor
2452 ::dbus::utility::getDbusObject(
2453 sensorPath, interfaces,
2454 [asyncResp, sensorId,
2455 sensorPath](const boost::system::error_code& ec,
2456 const ::dbus::utility::MapperGetObject& subtree) {
2457 BMCWEB_LOG_DEBUG("respHandler1 enter");
2458 if (ec == boost::system::errc::io_error)
2459 {
2460 BMCWEB_LOG_WARNING("Sensor not found from getSensorPaths");
2461 messages::resourceNotFound(asyncResp->res, sensorId, "Sensor");
2462 return;
2463 }
2464 if (ec)
2465 {
2466 messages::internalError(asyncResp->res);
2467 BMCWEB_LOG_ERROR(
2468 "Sensor getSensorPaths resp_handler: Dbus error {}", ec);
2469 return;
2470 }
2471 getSensorFromDbus(asyncResp, sensorPath, subtree);
2472 BMCWEB_LOG_DEBUG("respHandler1 exit");
2473 });
2474 }
2475
2476 } // namespace sensors
2477
requestRoutesSensorCollection(App & app)2478 inline void requestRoutesSensorCollection(App& app)
2479 {
2480 BMCWEB_ROUTE(app, "/redfish/v1/Chassis/<str>/Sensors/")
2481 .privileges(redfish::privileges::getSensorCollection)
2482 .methods(boost::beast::http::verb::get)(
2483 std::bind_front(sensors::handleSensorCollectionGet, std::ref(app)));
2484 }
2485
requestRoutesSensor(App & app)2486 inline void requestRoutesSensor(App& app)
2487 {
2488 BMCWEB_ROUTE(app, "/redfish/v1/Chassis/<str>/Sensors/<str>/")
2489 .privileges(redfish::privileges::getSensor)
2490 .methods(boost::beast::http::verb::get)(
2491 std::bind_front(sensors::handleSensorGet, std::ref(app)));
2492 }
2493
2494 } // namespace redfish
2495