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