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