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