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